processedCustomMaterial.cpp
Engine/source/materials/processedCustomMaterial.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/processedCustomMaterial.h" 26 27#include "gfx/sim/cubemapData.h" 28#include "materials/sceneData.h" 29#include "shaderGen/shaderGenVars.h" 30#include "scene/sceneRenderState.h" 31#include "materials/customMaterialDefinition.h" 32#include "materials/shaderData.h" 33#include "materials/materialManager.h" 34#include "materials/matTextureTarget.h" 35#include "materials/materialFeatureTypes.h" 36#include "materials/materialParameters.h" 37#include "gfx/sim/gfxStateBlockData.h" 38#include "core/util/safeDelete.h" 39#include "gfx/genericConstBuffer.h" 40#include "console/simFieldDictionary.h" 41#include "console/propertyParsing.h" 42#include "gfx/util/screenspace.h" 43#include "scene/reflectionManager.h" 44#include "renderInstance/renderProbeMgr.h" 45 46 47ProcessedCustomMaterial::ProcessedCustomMaterial(Material &mat) 48{ 49 mMaterial = &mat; 50 AssertFatal(dynamic_cast<CustomMaterial*>(mMaterial), "Incompatible Material type!"); 51 mCustomMaterial = static_cast<CustomMaterial*>(mMaterial); 52 mHasSetStageData = false; 53 mHasGlow = false; 54 mHasAccumulation = false; 55 mMaxStages = 0; 56 mMaxTex = 0; 57} 58 59ProcessedCustomMaterial::~ProcessedCustomMaterial() 60{ 61} 62 63void ProcessedCustomMaterial::_setStageData() 64{ 65 // Only do this once 66 if ( mHasSetStageData ) 67 return; 68 mHasSetStageData = true; 69 70 ShaderRenderPassData* rpd = _getRPD(0); 71 mConditionerMacros.clear(); 72 73 // Loop through all the possible textures, set the right flags, and load them if needed 74 for(U32 i=0; i<CustomMaterial::MAX_TEX_PER_PASS; i++ ) 75 { 76 rpd->mTexType[i] = Material::NoTexture; // Set none as the default in case none of the cases below catch it. 77 String filename = mCustomMaterial->mTexFilename[i]; 78 79 if(filename.isEmpty()) 80 continue; 81 82 if(filename.equal(String("$dynamiclight"), String::NoCase)) 83 { 84 rpd->mTexType[i] = Material::DynamicLight; 85 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 86 mMaxTex = i+1; 87 continue; 88 } 89 90 if(filename.equal(String("$dynamiclightmask"), String::NoCase)) 91 { 92 rpd->mTexType[i] = Material::DynamicLightMask; 93 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 94 mMaxTex = i+1; 95 continue; 96 } 97 98 if(filename.equal(String("$lightmap"), String::NoCase)) 99 { 100 rpd->mTexType[i] = Material::Lightmap; 101 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 102 mMaxTex = i+1; 103 continue; 104 } 105 106 if(filename.equal(String("$cubemap"), String::NoCase)) 107 { 108 if( mCustomMaterial->mCubemapData ) 109 { 110 rpd->mTexType[i] = Material::Cube; 111 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 112 mMaxTex = i+1; 113 } 114 else 115 { 116 mCustomMaterial->logError( "Could not find CubemapData - %s", mCustomMaterial->mCubemapName.c_str()); 117 } 118 continue; 119 } 120 121 if(filename.equal(String("$dynamicCubemap"), String::NoCase)) 122 { 123 rpd->mTexType[i] = Material::SGCube; 124 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 125 mMaxTex = i+1; 126 continue; 127 } 128 129 if(filename.equal(String("$backbuff"), String::NoCase)) 130 { 131 rpd->mTexType[i] = Material::BackBuff; 132 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 133 mMaxTex = i+1; 134 continue; 135 } 136 137 if(filename.equal(String("$reflectbuff"), String::NoCase)) 138 { 139 rpd->mTexType[i] = Material::ReflectBuff; 140 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 141 mMaxTex = i+1; 142 continue; 143 } 144 145 if(filename.equal(String("$miscbuff"), String::NoCase)) 146 { 147 rpd->mTexType[i] = Material::Misc; 148 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 149 mMaxTex = i+1; 150 continue; 151 } 152 153 // Check for a RenderTexTargetBin assignment 154 if (filename.substr( 0, 1 ).equal("#")) 155 { 156 String texTargetBufferName = filename.substr(1, filename.length() - 1); 157 NamedTexTarget *texTarget = NamedTexTarget::find( texTargetBufferName ); 158 rpd->mTexSlot[i].texTarget = texTarget; 159 160 // Get the conditioner macros. 161 if ( texTarget ) 162 texTarget->getShaderMacros( &mConditionerMacros ); 163 164 rpd->mTexType[i] = Material::TexTarget; 165 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 166 mMaxTex = i+1; 167 continue; 168 } 169 170 rpd->mTexSlot[i].texObject = _createTexture( filename, &GFXStaticTextureSRGBProfile ); 171 if ( !rpd->mTexSlot[i].texObject ) 172 { 173 mMaterial->logError("Failed to load texture %s", _getTexturePath(filename).c_str()); 174 continue; 175 } 176 rpd->mTexType[i] = Material::Standard; 177 rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i]; 178 mMaxTex = i+1; 179 } 180 181 // We only get one cubemap 182 if( mCustomMaterial->mCubemapData ) 183 { 184 mCustomMaterial->mCubemapData->createMap(); 185 rpd->mCubeMap = mMaterial->mCubemapData->mCubemap; // BTRTODO ? 186 if ( !rpd->mCubeMap ) 187 mMaterial->logError("Failed to load cubemap"); 188 } 189 190 // If this has a output target defined, it may be writing 191 // to a tex target bin with a conditioner, so search for 192 // one and add its macros. 193 if ( mCustomMaterial->mOutputTarget.isNotEmpty() ) 194 { 195 NamedTexTarget *texTarget = NamedTexTarget::find( mCustomMaterial->mOutputTarget ); 196 if ( texTarget ) 197 texTarget->getShaderMacros( &mConditionerMacros ); 198 } 199 200 // Copy the glow state over. 201 mHasGlow = mCustomMaterial->mGlow[0]; 202} 203 204bool ProcessedCustomMaterial::init( const FeatureSet &features, 205 const GFXVertexFormat *vertexFormat, 206 const MatFeaturesDelegate &featuresDelegate ) 207{ 208 // If we don't have a shader data... we have nothing to do. 209 if ( !mCustomMaterial->mShaderData ) 210 return true; 211 212 // Custom materials only do one pass at the moment... so 213 // add one for the stage data to fill in. 214 ShaderRenderPassData *rpd = new ShaderRenderPassData(); 215 mPasses.push_back( rpd ); 216 217 _setStageData(); 218 _initPassStateBlocks(); 219 mStateHint.clear(); 220 221 // Note: We don't use the vertex format in a custom 222 // material at all right now. 223 // 224 // Maybe we can add some required semantics and 225 // validate that the format fits the shader? 226 227 // Build a composite list of shader macros from 228 // the conditioner and the user defined lists. 229 Vector<GFXShaderMacro> macros; 230 macros.merge( mConditionerMacros ); 231 macros.merge( mUserMacros ); 232 233 // Ask the shader data to give us a shader instance. 234 rpd->shader = mCustomMaterial->mShaderData->getShader( macros ); 235 if ( !rpd->shader ) 236 { 237 delete rpd; 238 mPasses.clear(); 239 return false; 240 } 241 242 rpd->shaderHandles.init( rpd->shader, mCustomMaterial ); 243 _initMaterialParameters(); 244 mDefaultParameters = allocMaterialParameters(); 245 setMaterialParameters( mDefaultParameters, 0 ); 246 mStateHint.init( this ); 247 248 for(int i = 0; i < mMaxTex; i++) 249 { 250 ShaderConstHandles *handles = _getShaderConstHandles( mPasses.size()-1 ); 251 AssertFatal(handles,""); 252 253 if(rpd->mSamplerNames[i].isEmpty()) 254 continue; 255 256 String samplerName = rpd->mSamplerNames[i].startsWith("$") ? rpd->mSamplerNames[i] : String("$") + rpd->mSamplerNames[i]; 257 GFXShaderConstHandle *handle = rpd->shader->getShaderConstHandle( samplerName ); 258 AssertFatal(handle,""); 259 handles->mTexHandlesSC[i] = handle; 260 } 261 262 return true; 263} 264 265void ProcessedCustomMaterial::_initPassStateBlock( RenderPassData *rpd, GFXStateBlockDesc &result ) 266{ 267 Parent::_initPassStateBlock( rpd, result ); 268 269 if (mCustomMaterial->getStateBlockData()) 270 result.addDesc(mCustomMaterial->getStateBlockData()->getState()); 271} 272 273void ProcessedCustomMaterial::_initPassStateBlocks() 274{ 275 AssertFatal(mHasSetStageData, "State data must be set before initializing state block!"); 276 ShaderRenderPassData* rpd = _getRPD(0); 277 _initRenderStateStateBlocks( rpd ); 278} 279 280bool ProcessedCustomMaterial::_hasCubemap(U32 pass) 281{ 282 // If the material doesn't have a cubemap, we don't 283 if( mMaterial->mCubemapData ) return true; 284 else return false; 285} 286 287bool ProcessedCustomMaterial::setupPass( SceneRenderState *state, const SceneData& sgData, U32 pass ) 288{ 289 PROFILE_SCOPE( ProcessedCustomMaterial_SetupPass ); 290 291 // Make sure we have a pass. 292 if ( pass >= mPasses.size() ) 293 return false; 294 295 ShaderRenderPassData* rpd = _getRPD( pass ); 296 U32 currState = _getRenderStateIndex( state, sgData ); 297 GFX->setStateBlock(rpd->mRenderStates[currState]); 298 299 // activate shader 300 if ( rpd->shader ) 301 GFX->setShader( rpd->shader ); 302 else 303 GFX->setupGenericShaders(); 304 305 // Set our textures 306 setTextureStages( state, sgData, pass ); 307 308 GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); 309 GFX->setShaderConstBuffer(shaderConsts); 310 311 // Set our shader constants. 312 _setTextureTransforms(pass); 313 _setShaderConstants(state, sgData, pass); 314 315 LightManager* lm = state ? LIGHTMGR : NULL; 316 if (lm) 317 lm->setLightInfo(this, NULL, sgData, state, pass, shaderConsts); 318 319 RenderProbeMgr* pm = state ? PROBEMGR : NULL; 320 if (pm) 321 pm->setProbeInfo(this, NULL, sgData, state, pass, shaderConsts); 322 323 shaderConsts->setSafe(rpd->shaderHandles.mAccumTimeSC, MATMGR->getTotalTime()); 324 325 return true; 326} 327 328void ProcessedCustomMaterial::setTextureStages( SceneRenderState *state, const SceneData &sgData, U32 pass ) 329{ 330 LightManager* lm = state ? LIGHTMGR : NULL; 331 ShaderRenderPassData* rpd = _getRPD(pass); 332 ShaderConstHandles* handles = _getShaderConstHandles(pass); 333 GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); 334 335 const NamedTexTarget *texTarget; 336 GFXTextureObject *texObject; 337 338 for( U32 i=0; i<mMaxTex; i++ ) 339 { 340 U32 currTexFlag = rpd->mTexType[i]; 341 if ( !lm || !lm->setTextureStage(sgData, currTexFlag, i, shaderConsts, handles ) ) 342 { 343 GFXShaderConstHandle* handle = handles->mTexHandlesSC[i]; 344 if ( !handle->isValid() ) 345 continue; 346 347 S32 samplerRegister = handle->getSamplerRegister(); 348 349 switch( currTexFlag ) 350 { 351 case 0: 352 default: 353 break; 354 355 case Material::Mask: 356 case Material::Standard: 357 case Material::Bump: 358 case Material::Detail: 359 { 360 GFX->setTexture( samplerRegister, rpd->mTexSlot[i].texObject ); 361 break; 362 } 363 364 case Material::Lightmap: 365 { 366 GFX->setTexture( samplerRegister, sgData.lightmap ); 367 break; 368 } 369 case Material::Cube: 370 { 371 GFX->setCubeTexture( samplerRegister, rpd->mCubeMap ); 372 break; 373 } 374 case Material::SGCube: 375 { 376 GFX->setCubeTexture( samplerRegister, sgData.cubemap ); 377 break; 378 } 379 case Material::BackBuff: 380 { 381 GFX->setTexture( samplerRegister, sgData.backBuffTex ); 382 //if ( sgData.reflectTex ) 383 // GFX->setTexture( samplerRegister, sgData.reflectTex ); 384 //else 385 //{ 386 // GFXTextureObject *refractTex = REFLECTMGR->getRefractTex( true ); 387 // GFX->setTexture( samplerRegister, refractTex ); 388 //} 389 break; 390 } 391 case Material::ReflectBuff: 392 { 393 GFX->setTexture( samplerRegister, sgData.reflectTex ); 394 break; 395 } 396 case Material::Misc: 397 { 398 GFX->setTexture( samplerRegister, sgData.miscTex ); 399 break; 400 } 401 case Material::TexTarget: 402 { 403 texTarget = rpd->mTexSlot[i].texTarget; 404 if ( !texTarget ) 405 { 406 GFX->setTexture( samplerRegister, NULL ); 407 break; 408 } 409 410 texObject = texTarget->getTexture(); 411 412 // If no texture is available then map the default 2x2 413 // black texture to it. This at least will ensure that 414 // we get consistant behavior across GPUs and platforms. 415 if ( !texObject ) 416 texObject = GFXTexHandle::ZERO; 417 418 if ( handles->mRTParamsSC[samplerRegister]->isValid() && texObject ) 419 { 420 const Point3I &targetSz = texObject->getSize(); 421 const RectI &targetVp = texTarget->getViewport(); 422 Point4F rtParams; 423 424 ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams); 425 shaderConsts->set(handles->mRTParamsSC[samplerRegister], rtParams); 426 } 427 428 GFX->setTexture( samplerRegister, texObject ); 429 break; 430 } 431 } 432 } 433 } 434} 435 436template <typename T> 437void ProcessedCustomMaterial::setMaterialParameter(MaterialParameters* param, 438 MaterialParameterHandle* handle, 439 const String& value) 440{ 441 T typedValue; 442 if (PropertyInfo::default_scan(value, typedValue)) 443 { 444 param->set(handle, typedValue); 445 } else { 446 Con::errorf("Error setting %s, parse error: %s", handle->getName().c_str(), value.c_str()); 447 } 448} 449 450void ProcessedCustomMaterial::setMatrixParameter(MaterialParameters* param, 451 MaterialParameterHandle* handle, 452 const String& value, GFXShaderConstType matrixType) 453{ 454 MatrixF result(true); 455 F32* m = result; 456 switch (matrixType) 457 { 458 case GFXSCT_Float2x2 : 459 dSscanf(value.c_str(),"%g %g %g %g", 460 m[result.idx(0,0)], m[result.idx(0,1)], 461 m[result.idx(1,0)], m[result.idx(1,1)]); 462 break; 463 case GFXSCT_Float3x3 : 464 dSscanf(value.c_str(),"%g %g %g %g %g %g %g %g %g", 465 m[result.idx(0,0)], m[result.idx(0,1)], m[result.idx(0,2)], 466 m[result.idx(1,0)], m[result.idx(1,1)], m[result.idx(1,2)], 467 m[result.idx(2,0)], m[result.idx(2,1)], m[result.idx(2,2)]); 468 break; 469 default: 470 AssertFatal(false, "Invalid type!"); 471 break; 472 } 473} 474 475// BTRTODO: Support arrays!? 476MaterialParameters* ProcessedCustomMaterial::allocMaterialParameters() 477{ 478 MaterialParameters* ret = Parent::allocMaterialParameters(); 479 // See if any of the dynamic fields match up with shader constants we have. 480 SimFieldDictionary* fields = mMaterial->getFieldDictionary(); 481 if (!fields || fields->getNumFields() == 0) 482 return ret; 483 484 const Vector<GFXShaderConstDesc>& consts = ret->getShaderConstDesc(); 485 for (U32 i = 0; i < consts.size(); i++) 486 { 487 // strip the dollar sign from the front. 488 String stripped(consts[i].name); 489 stripped.erase(0, 1); 490 491 SimFieldDictionary::Entry* field = fields->findDynamicField(stripped); 492 if (field) 493 { 494 MaterialParameterHandle* handle = getMaterialParameterHandle(consts[i].name); 495 switch (consts[i].constType) 496 { 497 case GFXSCT_Float : 498 setMaterialParameter<F32>(ret, handle, field->value); 499 break; 500 case GFXSCT_Float2: 501 setMaterialParameter<Point2F>(ret, handle, field->value); 502 break; 503 case GFXSCT_Float3: 504 setMaterialParameter<Point3F>(ret, handle, field->value); 505 break; 506 case GFXSCT_Float4: 507 setMaterialParameter<Point4F>(ret, handle, field->value); 508 break; 509 case GFXSCT_Float2x2: 510 case GFXSCT_Float3x3: 511 setMatrixParameter(ret, handle, field->value, consts[i].constType); 512 break; 513 case GFXSCT_Float4x4: 514 setMaterialParameter<MatrixF>(ret, handle, field->value); 515 break; 516 case GFXSCT_Int: 517 setMaterialParameter<S32>(ret, handle, field->value); 518 break; 519 case GFXSCT_Int2: 520 setMaterialParameter<Point2I>(ret, handle, field->value); 521 break; 522 case GFXSCT_Int3: 523 setMaterialParameter<Point3I>(ret, handle, field->value); 524 break; 525 case GFXSCT_Int4: 526 setMaterialParameter<Point4I>(ret, handle, field->value); 527 break; 528 // Do we want to ignore these? 529 case GFXSCT_Sampler: 530 case GFXSCT_SamplerCube: 531 default: 532 break; 533 } 534 } 535 } 536 return ret; 537} 538