processedMaterial.cpp
Engine/source/materials/processedMaterial.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "materials/processedMaterial.h" 26 27#include "materials/sceneData.h" 28#include "materials/materialParameters.h" 29#include "materials/matTextureTarget.h" 30#include "materials/materialFeatureTypes.h" 31#include "materials/materialManager.h" 32#include "scene/sceneRenderState.h" 33#include "gfx/gfxPrimitiveBuffer.h" 34#include "gfx/gfxTextureManager.h" 35#include "gfx/sim/cubemapData.h" 36 37RenderPassData::RenderPassData() 38{ 39 reset(); 40} 41 42void RenderPassData::reset() 43{ 44 for( U32 i = 0; i < Material::MAX_TEX_PER_PASS; ++ i ) 45 { 46 destructInPlace( &mTexSlot[ i ] ); 47 mSamplerNames[ i ].clear(); 48 } 49 50 dMemset( &mTexSlot, 0, sizeof(mTexSlot) ); 51 dMemset( &mTexType, 0, sizeof(mTexType) ); 52 53 mCubeMap = NULL; 54 mNumTex = mNumTexReg = mStageNum = 0; 55 mGlow = false; 56 mBlendOp = Material::None; 57 58 mFeatureData.clear(); 59 60 for (U32 i = 0; i < STATE_MAX; i++) 61 mRenderStates[i] = NULL; 62} 63 64String RenderPassData::describeSelf() const 65{ 66 String desc; 67 68 // Now write all the textures. 69 String texName; 70 for ( U32 i=0; i < Material::MAX_TEX_PER_PASS; i++ ) 71 { 72 if ( mTexType[i] == Material::TexTarget ) 73 texName = ( mTexSlot[i].texTarget ) ? mTexSlot[i].texTarget->getName() : "null_texTarget"; 74 else if ( mTexType[i] == Material::Cube && mCubeMap ) 75 texName = mCubeMap->getPath(); 76 else if ( mTexSlot[i].texObject ) 77 texName = mTexSlot[i].texObject->getPath(); 78 else 79 continue; 80 81 desc += String::ToString( "TexSlot %d: %d, %s\n", i, mTexType[i], texName.c_str() ); 82 } 83 84 // Write out the first render state which is the 85 // basis for all the other states and shoud be 86 // enough to define the pass uniquely. 87 desc += mRenderStates[0]->getDesc().describeSelf(); 88 89 return desc; 90} 91 92ProcessedMaterial::ProcessedMaterial() 93: mMaterial( NULL ), 94 mCurrentParams( NULL ), 95 mHasSetStageData( false ), 96 mHasGlow( false ), 97 mHasAccumulation( false ), 98 mMaxStages( 0 ), 99 mVertexFormat( NULL ), 100 mUserObject( NULL ) 101{ 102 VECTOR_SET_ASSOCIATION( mPasses ); 103} 104 105ProcessedMaterial::~ProcessedMaterial() 106{ 107 T3D::for_each( mPasses.begin(), mPasses.end(), T3D::delete_pointer() ); 108} 109 110void ProcessedMaterial::_setBlendState(Material::BlendOp blendOp, GFXStateBlockDesc& desc ) 111{ 112 switch( blendOp ) 113 { 114 case Material::Add: 115 { 116 desc.blendSrc = GFXBlendOne; 117 desc.blendDest = GFXBlendOne; 118 break; 119 } 120 case Material::AddAlpha: 121 { 122 desc.blendSrc = GFXBlendSrcAlpha; 123 desc.blendDest = GFXBlendOne; 124 break; 125 } 126 case Material::Mul: 127 { 128 desc.blendSrc = GFXBlendDestColor; 129 desc.blendDest = GFXBlendInvSrcAlpha; 130 break; 131 } 132 case Material::PreMul: 133 { 134 desc.blendSrc = GFXBlendOne; 135 desc.blendDest = GFXBlendInvSrcAlpha; 136 break; 137 } 138 case Material::LerpAlpha: 139 { 140 desc.blendSrc = GFXBlendSrcAlpha; 141 desc.blendDest = GFXBlendInvSrcAlpha; 142 break; 143 } 144 case Material::Sub: 145 { 146 desc.blendOp = GFXBlendOpSubtract; 147 desc.blendSrc = GFXBlendOne; 148 desc.blendDest = GFXBlendOne; 149 break; 150 } 151 152 default: 153 { 154 // default to LerpAlpha 155 desc.blendSrc = GFXBlendSrcAlpha; 156 desc.blendDest = GFXBlendInvSrcAlpha; 157 break; 158 } 159 } 160} 161 162void ProcessedMaterial::setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer) 163{ 164 GFX->setVertexBuffer( *vertBuffer ); 165 GFX->setPrimitiveBuffer( *primBuffer ); 166} 167 168bool ProcessedMaterial::stepInstance() 169{ 170 AssertFatal( false, "ProcessedMaterial::stepInstance() - This type of material doesn't support instancing!" ); 171 return false; 172} 173 174String ProcessedMaterial::_getTexturePath(const String& filename) 175{ 176 // if '/', then path is specified, use it. 177 if( filename.find('/') != String::NPos ) 178 { 179 return filename; 180 } 181 182 // otherwise, construct path 183 return mMaterial->getPath() + filename; 184} 185 186GFXTexHandle ProcessedMaterial::_createTexture( const char* filename, GFXTextureProfile *profile) 187{ 188 return GFXTexHandle( _getTexturePath(filename), profile, avar("%s() - NA (line %d)", __FUNCTION__, __LINE__) ); 189} 190 191GFXTexHandle ProcessedMaterial::_createCompositeTexture(const char *filenameR, const char *filenameG, const char *filenameB, const char *filenameA, U32 inputKey[4], GFXTextureProfile *profile) 192{ 193 return GFXTexHandle(_getTexturePath(filenameR), _getTexturePath(filenameG), _getTexturePath(filenameB), _getTexturePath(filenameA), inputKey, profile, avar("%s() - NA (line %d)", __FUNCTION__, __LINE__)); 194} 195 196void ProcessedMaterial::addStateBlockDesc(const GFXStateBlockDesc& sb) 197{ 198 mUserDefined = sb; 199} 200 201void ProcessedMaterial::_initStateBlockTemplates(GFXStateBlockDesc& stateTranslucent, GFXStateBlockDesc& stateGlow, GFXStateBlockDesc& stateReflect) 202{ 203 // Translucency 204 stateTranslucent.blendDefined = true; 205 stateTranslucent.blendEnable = mMaterial->mTranslucentBlendOp != Material::None; 206 _setBlendState(mMaterial->mTranslucentBlendOp, stateTranslucent); 207 stateTranslucent.zDefined = true; 208 stateTranslucent.zWriteEnable = mMaterial->mTranslucentZWrite; 209 stateTranslucent.alphaDefined = true; 210 stateTranslucent.alphaTestEnable = mMaterial->mAlphaTest; 211 stateTranslucent.alphaTestRef = mMaterial->mAlphaRef; 212 stateTranslucent.alphaTestFunc = GFXCmpGreaterEqual; 213 stateTranslucent.samplersDefined = true; 214 215 // Glow 216 stateGlow.zDefined = true; 217 stateGlow.zWriteEnable = false; 218 219 // Reflect 220 stateReflect.cullDefined = true; 221 stateReflect.cullMode = mMaterial->mDoubleSided ? GFXCullNone : GFXCullCW; 222} 223 224void ProcessedMaterial::_initRenderPassDataStateBlocks() 225{ 226 for (U32 pass = 0; pass < mPasses.size(); pass++) 227 _initRenderStateStateBlocks( mPasses[pass] ); 228} 229 230void ProcessedMaterial::_initPassStateBlock( RenderPassData *rpd, GFXStateBlockDesc &result ) 231{ 232 if ( rpd->mBlendOp != Material::None ) 233 { 234 result.blendDefined = true; 235 result.blendEnable = true; 236 _setBlendState( rpd->mBlendOp, result ); 237 } 238 239 if (mMaterial && mMaterial->isDoubleSided()) 240 { 241 result.cullDefined = true; 242 result.cullMode = GFXCullNone; 243 } 244 245 if(mMaterial && mMaterial->mAlphaTest) 246 { 247 result.alphaDefined = true; 248 result.alphaTestEnable = mMaterial->mAlphaTest; 249 result.alphaTestRef = mMaterial->mAlphaRef; 250 result.alphaTestFunc = GFXCmpGreaterEqual; 251 } 252 253 result.samplersDefined = true; 254 NamedTexTarget *texTarget; 255 256 U32 maxAnisotropy = 1; 257 if (mMaterial && mMaterial->mUseAnisotropic[ rpd->mStageNum ] ) 258 maxAnisotropy = MATMGR->getDefaultAnisotropy(); 259 260 for( U32 i=0; i < rpd->mNumTex; i++ ) 261 { 262 U32 currTexFlag = rpd->mTexType[i]; 263 264 switch( currTexFlag ) 265 { 266 default: 267 { 268 result.samplers[i].addressModeU = GFXAddressWrap; 269 result.samplers[i].addressModeV = GFXAddressWrap; 270 271 if ( maxAnisotropy > 1 ) 272 { 273 result.samplers[i].minFilter = GFXTextureFilterAnisotropic; 274 result.samplers[i].magFilter = GFXTextureFilterAnisotropic; 275 result.samplers[i].maxAnisotropy = maxAnisotropy; 276 } 277 else 278 { 279 result.samplers[i].minFilter = GFXTextureFilterLinear; 280 result.samplers[i].magFilter = GFXTextureFilterLinear; 281 } 282 break; 283 } 284 285 case Material::Cube: 286 case Material::SGCube: 287 case Material::NormalizeCube: 288 { 289 result.samplers[i].addressModeU = GFXAddressClamp; 290 result.samplers[i].addressModeV = GFXAddressClamp; 291 result.samplers[i].addressModeW = GFXAddressClamp; 292 result.samplers[i].minFilter = GFXTextureFilterLinear; 293 result.samplers[i].magFilter = GFXTextureFilterLinear; 294 break; 295 } 296 297 case Material::TexTarget: 298 { 299 texTarget = mPasses[0]->mTexSlot[i].texTarget; 300 if ( texTarget ) 301 texTarget->setupSamplerState( &result.samplers[i] ); 302 break; 303 } 304 } 305 } 306 307 // The deferred will take care of writing to the 308 // zbuffer, so we don't have to by default. 309 if ( MATMGR->getDeferredEnabled() && 310 !mFeatures.hasFeature(MFT_ForwardShading)) 311 result.setZReadWrite( result.zEnable, false ); 312 313 result.addDesc(mUserDefined); 314} 315 316/// Creates the default state blocks for a list of render states 317void ProcessedMaterial::_initRenderStateStateBlocks( RenderPassData *rpd ) 318{ 319 GFXStateBlockDesc stateTranslucent; 320 GFXStateBlockDesc stateGlow; 321 GFXStateBlockDesc stateReflect; 322 GFXStateBlockDesc statePass; 323 324 _initStateBlockTemplates( stateTranslucent, stateGlow, stateReflect ); 325 _initPassStateBlock( rpd, statePass ); 326 327 // Ok, we've got our templates set up, let's combine them together based on state and 328 // create our state blocks. 329 for (U32 i = 0; i < RenderPassData::STATE_MAX; i++) 330 { 331 GFXStateBlockDesc stateFinal; 332 333 if (i & RenderPassData::STATE_REFLECT) 334 stateFinal.addDesc(stateReflect); 335 if (i & RenderPassData::STATE_TRANSLUCENT) 336 stateFinal.addDesc(stateTranslucent); 337 if (i & RenderPassData::STATE_GLOW) 338 stateFinal.addDesc(stateGlow); 339 340 stateFinal.addDesc(statePass); 341 342 if (i & RenderPassData::STATE_WIREFRAME) 343 stateFinal.fillMode = GFXFillWireframe; 344 345 GFXStateBlockRef sb = GFX->createStateBlock(stateFinal); 346 rpd->mRenderStates[i] = sb; 347 } 348} 349 350U32 ProcessedMaterial::_getRenderStateIndex( const SceneRenderState *sceneState, 351 const SceneData &sgData ) 352{ 353 // Based on what the state of the world is, get our render state block 354 U32 currState = 0; 355 356 // NOTE: We should only use per-material or per-pass hints to 357 // change the render state. This is importaint because we 358 // only change the state blocks between material passes. 359 // 360 // For example sgData.visibility would be bad to use 361 // in here without changing how RenderMeshMgr works. 362 363 if ( sgData.binType == SceneData::GlowBin ) 364 currState |= RenderPassData::STATE_GLOW; 365 366 if ( sceneState && sceneState->isReflectPass() ) 367 currState |= RenderPassData::STATE_REFLECT; 368 369 if ( sgData.binType != SceneData::DeferredBin && 370 mMaterial->isTranslucent() ) 371 currState |= RenderPassData::STATE_TRANSLUCENT; 372 373 if ( sgData.wireframe ) 374 currState |= RenderPassData::STATE_WIREFRAME; 375 376 return currState; 377} 378 379void ProcessedMaterial::_setRenderState( const SceneRenderState *state, 380 const SceneData& sgData, 381 U32 pass ) 382{ 383 // Make sure we have the pass 384 if ( pass >= mPasses.size() ) 385 return; 386 387 U32 currState = _getRenderStateIndex( state, sgData ); 388 389 GFX->setStateBlock(mPasses[pass]->mRenderStates[currState]); 390} 391 392 393void ProcessedMaterial::_setStageData() 394{ 395 // Only do this once 396 if (mHasSetStageData) 397 return; 398 mHasSetStageData = true; 399 400 U32 i; 401 402 // Load up all the textures for every possible stage 403 for (i = 0; i < Material::MAX_STAGES; i++) 404 { 405 // DiffuseMap 406 if (mMaterial->mDiffuseMapFilename[i].isNotEmpty()) 407 { 408 mStages[i].setTex(MFT_DiffuseMap, _createTexture(mMaterial->mDiffuseMapFilename[i], &GFXStaticTextureSRGBProfile)); 409 if (!mStages[i].getTex(MFT_DiffuseMap)) 410 { 411 //If we start with a #, we're probably actually attempting to hit a named target and it may not get a hit on the first pass. So we'll 412 //pass on the error rather than spamming the console 413 if (!mMaterial->mDiffuseMapFilename[i].startsWith("#")) 414 mMaterial->logError("Failed to load diffuse map %s for stage %i", _getTexturePath(mMaterial->mDiffuseMapFilename[i]).c_str(), i); 415 416 // Load a debug texture to make it clear to the user 417 // that the texture for this stage was missing. 418 mStages[i].setTex(MFT_DiffuseMap, _createTexture(GFXTextureManager::getMissingTexturePath().c_str(), &GFXStaticTextureSRGBProfile)); 419 } 420 } 421 else if (mMaterial->mDiffuseMapAsset[i] && !mMaterial->mDiffuseMapAsset[i].isNull()) 422 { 423 mStages[i].setTex(MFT_DiffuseMap, mMaterial->mDiffuseMapAsset[i]->getImage(GFXStaticTextureSRGBProfile)); 424 if (!mStages[i].getTex(MFT_DiffuseMap)) 425 { 426 // Load a debug texture to make it clear to the user 427 // that the texture for this stage was missing. 428 mStages[i].setTex(MFT_DiffuseMap, _createTexture(GFXTextureManager::getMissingTexturePath().c_str(), &GFXStaticTextureSRGBProfile)); 429 } 430 } 431 432 // OverlayMap 433 if (mMaterial->mOverlayMapFilename[i].isNotEmpty()) 434 { 435 mStages[i].setTex(MFT_OverlayMap, _createTexture(mMaterial->mOverlayMapFilename[i], &GFXStaticTextureSRGBProfile)); 436 if (!mStages[i].getTex(MFT_OverlayMap)) 437 mMaterial->logError("Failed to load overlay map %s for stage %i", _getTexturePath(mMaterial->mOverlayMapFilename[i]).c_str(), i); 438 } 439 440 // LightMap 441 if (mMaterial->mLightMapFilename[i].isNotEmpty()) 442 { 443 mStages[i].setTex(MFT_LightMap, _createTexture(mMaterial->mLightMapFilename[i], &GFXStaticTextureSRGBProfile)); 444 if (!mStages[i].getTex(MFT_LightMap)) 445 mMaterial->logError("Failed to load light map %s for stage %i", _getTexturePath(mMaterial->mLightMapFilename[i]).c_str(), i); 446 } 447 448 // ToneMap 449 if (mMaterial->mToneMapFilename[i].isNotEmpty()) 450 { 451 mStages[i].setTex(MFT_ToneMap, _createTexture(mMaterial->mToneMapFilename[i], &GFXStaticTextureProfile)); 452 if (!mStages[i].getTex(MFT_ToneMap)) 453 mMaterial->logError("Failed to load tone map %s for stage %i", _getTexturePath(mMaterial->mToneMapFilename[i]).c_str(), i); 454 } 455 456 // DetailMap 457 if (mMaterial->mDetailMapFilename[i].isNotEmpty()) 458 { 459 mStages[i].setTex(MFT_DetailMap, _createTexture(mMaterial->mDetailMapFilename[i], &GFXStaticTextureProfile)); 460 if (!mStages[i].getTex(MFT_DetailMap)) 461 mMaterial->logError("Failed to load detail map %s for stage %i", _getTexturePath(mMaterial->mDetailMapFilename[i]).c_str(), i); 462 } 463 464 // NormalMap 465 if (mMaterial->mNormalMapFilename[i].isNotEmpty()) 466 { 467 mStages[i].setTex(MFT_NormalMap, _createTexture(mMaterial->mNormalMapFilename[i], &GFXNormalMapProfile)); 468 if (!mStages[i].getTex(MFT_NormalMap)) 469 mMaterial->logError("Failed to load normal map %s for stage %i", _getTexturePath(mMaterial->mNormalMapFilename[i]).c_str(), i); 470 } 471 472 // Detail Normal Map 473 if (mMaterial->mDetailNormalMapFilename[i].isNotEmpty()) 474 { 475 mStages[i].setTex(MFT_DetailNormalMap, _createTexture(mMaterial->mDetailNormalMapFilename[i], &GFXNormalMapProfile)); 476 if (!mStages[i].getTex(MFT_DetailNormalMap)) 477 mMaterial->logError("Failed to load normal map %s for stage %i", _getTexturePath(mMaterial->mDetailNormalMapFilename[i]).c_str(), i); 478 } 479 480 GFXTextureProfile* profile = &GFXStaticTextureProfile; 481 if (mMaterial->mIsSRGb[i]) 482 profile = &GFXStaticTextureSRGBProfile; 483 484 // ORMConfig 485 if (mMaterial->mORMConfigMapFilename[i].isNotEmpty()) 486 { 487 mStages[i].setTex(MFT_OrmMap, _createTexture(mMaterial->mORMConfigMapFilename[i], profile)); 488 if (!mStages[i].getTex(MFT_OrmMap)) 489 mMaterial->logError("Failed to load PBR Config map %s for stage %i", _getTexturePath(mMaterial->mORMConfigMapFilename[i]).c_str(), i); 490 } 491 else 492 { 493 if (mMaterial->mRoughMapFilename[i].isNotEmpty() && mMaterial->mMetalMapFilename[i].isNotEmpty()) 494 { 495 U32 inputKey[4]; 496 inputKey[0] = mMaterial->mAOChan[i]; 497 inputKey[1] = mMaterial->mRoughnessChan[i]; 498 inputKey[2] = mMaterial->mMetalChan[i]; 499 inputKey[3] = 0; 500 mStages[i].setTex(MFT_OrmMap, _createCompositeTexture( mMaterial->mAOMapFilename[i], mMaterial->mRoughMapFilename[i], 501 mMaterial->mMetalMapFilename[i], "", 502 inputKey, profile)); 503 if (!mStages[i].getTex(MFT_OrmMap)) 504 mMaterial->logError("Failed to load PBR Config map %s for stage %i", _getTexturePath(mMaterial->mORMConfigMapFilename[i]).c_str(), i); 505 } 506 } 507 if (mMaterial->mGlowMapFilename[i].isNotEmpty()) 508 { 509 mStages[i].setTex(MFT_GlowMap, _createTexture(mMaterial->mGlowMapFilename[i], &GFXStaticTextureProfile)); 510 if (!mStages[i].getTex(MFT_GlowMap)) 511 mMaterial->logError("Failed to load glow map %s for stage %i", _getTexturePath(mMaterial->mGlowMapFilename[i]).c_str(), i); 512 } 513 } 514 515 mMaterial->mCubemapData = dynamic_cast<CubemapData*>(Sim::findObject(mMaterial->mCubemapName)); 516 if (!mMaterial->mCubemapData) 517 mMaterial->mCubemapData = NULL; 518 519 520 // If we have a cubemap put it on stage 0 (cubemaps only supported on stage 0) 521 if (mMaterial->mCubemapData) 522 { 523 mMaterial->mCubemapData->createMap(); 524 mStages[0].setCubemap(mMaterial->mCubemapData->mCubemap); 525 if (!mStages[0].getCubemap()) 526 mMaterial->logError("Failed to load cubemap"); 527 } 528} 529 530