bool RenderPrePassMgr::_updateTargets() { PROFILE_SCOPE(RenderPrePassMgr_updateTargets); bool ret = Parent::_updateTargets(); // check for an output conditioner, and update it's format ConditionerFeature *outputConditioner = dynamic_cast<ConditionerFeature *>(FEATUREMGR->getByType(MFT_PrePassConditioner)); if( outputConditioner && outputConditioner->setBufferFormat(mTargetFormat) ) { // reload materials, the conditioner needs to alter the generated shaders } // Attach the light info buffer as a second render target, if there is // lightmapped geometry in the scene. AdvancedLightBinManager *lightBin; if ( Sim::findObject( "AL_LightBinMgr", lightBin ) && lightBin->MRTLightmapsDuringPrePass() && lightBin->isProperlyAdded() ) { // Update the size of the light bin target here. This will call _updateTargets // on the light bin ret &= lightBin->setTargetSize( mTargetSize ); if ( ret ) { // Sanity check AssertFatal(lightBin->getTargetChainLength() == mTargetChainLength, "Target chain length mismatch"); // Attach light info buffer to Color1 for each target in the chain for ( U32 i = 0; i < mTargetChainLength; i++ ) { GFXTexHandle lightInfoTex = lightBin->getTargetTexture(0, i); mTargetChain[i]->attachTexture(GFXTextureTarget::Color1, lightInfoTex); } } } return ret; }
bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials, Pass *pass, bool firstPass, bool prePassMat, bool reflectMat, bool baseOnly ) { if ( GFX->getPixelShaderVersion() < 3.0f ) baseOnly = true; // NOTE: At maximum we only try to combine sgMaxTerrainMaterialsPerPass materials // into a single pass. This is sub-optimal for the simplest // cases, but the most common case results in much fewer // shader generation failures and permutations leading to // faster load time and less hiccups during gameplay. U32 matCount = getMin( sgMaxTerrainMaterialsPerPass, materials->size() ); Vector<GFXTexHandle> normalMaps; // See if we're currently running under the // basic lighting manager. // // TODO: This seems ugly... we should trigger // features like this differently in the future. // bool useBLM = dStrcmp( LIGHTMGR->getId(), "BLM" ) == 0; // Do we need to disable normal mapping? const bool disableNormalMaps = MATMGR->getExclusionFeatures().hasFeature( MFT_NormalMap ) || useBLM; // How about parallax? const bool disableParallaxMaps = GFX->getPixelShaderVersion() < 3.0f || MATMGR->getExclusionFeatures().hasFeature( MFT_Parallax ); // Has advanced lightmap support been enabled for prepass. bool advancedLightmapSupport = false; if ( prePassMat ) { // This sucks... but it works. AdvancedLightBinManager *lightBin; if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) advancedLightmapSupport = lightBin->MRTLightmapsDuringPrePass(); } // Loop till we create a valid shader! while( true ) { FeatureSet features; features.addFeature( MFT_VertTransform ); if ( prePassMat ) { features.addFeature( MFT_EyeSpaceDepthOut ); features.addFeature( MFT_PrePassConditioner ); features.addFeature( MFT_DeferredTerrainBaseMap ); features.addFeature(MFT_isDeferred); if ( advancedLightmapSupport ) features.addFeature( MFT_RenderTarget3_Zero ); } else { features.addFeature( MFT_TerrainBaseMap ); features.addFeature( MFT_RTLighting ); // The HDR feature is always added... it will compile out // if HDR is not enabled in the engine. features.addFeature( MFT_HDROut ); } features.addFeature(MFT_DeferredTerrainBlankInfoMap); // Enable lightmaps and fogging if we're in BL. if ( reflectMat || useBLM ) { features.addFeature( MFT_Fog ); features.addFeature( MFT_ForwardShading ); } if ( useBLM ) features.addFeature( MFT_TerrainLightMap ); // The additional passes need to be lerp blended into the // target to maintain the results of the previous passes. if ( !firstPass ) features.addFeature( MFT_TerrainAdditive ); normalMaps.clear(); pass->materials.clear(); // Now add all the material layer features. for ( U32 i=0; i < matCount && !baseOnly; i++ ) { TerrainMaterial *mat = (*materials)[i]->mat; if ( mat == NULL ) continue; // We only include materials that // have more than a base texture. if ( mat->getDetailSize() <= 0 || mat->getDetailDistance() <= 0 || mat->getDetailMap().isEmpty() ) continue; S32 featureIndex = pass->materials.size(); // check for macro detail texture if ( !(mat->getMacroSize() <= 0 || mat->getMacroDistance() <= 0 || mat->getMacroMap().isEmpty() ) ) { if(prePassMat) features.addFeature( MFT_DeferredTerrainMacroMap, featureIndex ); else features.addFeature( MFT_TerrainMacroMap, featureIndex ); } if(prePassMat) features.addFeature( MFT_DeferredTerrainDetailMap, featureIndex ); else features.addFeature( MFT_TerrainDetailMap, featureIndex ); pass->materials.push_back( (*materials)[i] ); normalMaps.increment(); // Skip normal maps if we need to. if ( !disableNormalMaps && mat->getNormalMap().isNotEmpty() ) { features.addFeature( MFT_TerrainNormalMap, featureIndex ); normalMaps.last().set( mat->getNormalMap(), &GFXDefaultStaticNormalMapProfile, "TerrainCellMaterial::_createPass() - NormalMap" ); if ( normalMaps.last().getFormat() == GFXFormatDXT5 ) features.addFeature( MFT_IsDXTnm, featureIndex ); // Do we need and can we do parallax mapping? if ( !disableParallaxMaps && mat->getParallaxScale() > 0.0f && !mat->useSideProjection() ) features.addFeature( MFT_TerrainParallaxMap, featureIndex ); } // Is this layer got side projection? if ( mat->useSideProjection() ) features.addFeature( MFT_TerrainSideProject, featureIndex ); } MaterialFeatureData featureData; featureData.features = features; featureData.materialFeatures = features; // Check to see how many vertex shader output // registers we're gonna need. U32 numTex = 0; U32 numTexReg = 0; for ( U32 i=0; i < features.getCount(); i++ ) { S32 index; const FeatureType &type = features.getAt( i, &index ); ShaderFeature* sf = FEATUREMGR->getByType( type ); if ( !sf ) continue; sf->setProcessIndex( index ); ShaderFeature::Resources res = sf->getResources( featureData ); numTex += res.numTex; numTexReg += res.numTexReg; } // Can we build the shader? // // NOTE: The 10 is sort of an abitrary SM 3.0 // limit. Its really supposed to be 11, but that // always fails to compile so far. // if ( numTex < GFX->getNumSamplers() && numTexReg <= 10 ) { // NOTE: We really shouldn't be getting errors building the shaders, // but we can generate more instructions than the ps_2_x will allow. // // There is no way to deal with this case that i know of other than // letting the compile fail then recovering by trying to build it // with fewer materials. // // We normally disable the shader error logging so that the user // isn't fooled into thinking there is a real bug. That is until // we get down to a single material. If a single material case // fails it means it cannot generate any passes at all! const bool logErrors = matCount == 1; GFXShader::setLogging( logErrors, true ); pass->shader = SHADERGEN->getShader( featureData, getGFXVertexFormat<TerrVertex>(), NULL, mSamplerNames ); } // If we got a shader then we can continue. if ( pass->shader ) break; // If the material count is already 1 then this // is a real shader error... give up! if ( matCount <= 1 ) return false; // If we failed we next try half the input materials // so that we can more quickly arrive at a valid shader. matCount -= matCount / 2; } // Setup the constant buffer. pass->consts = pass->shader->allocConstBuffer(); // Prepare the basic constants. pass->modelViewProjConst = pass->shader->getShaderConstHandle( "$modelview" ); pass->worldViewOnly = pass->shader->getShaderConstHandle( "$worldViewOnly" ); pass->viewToObj = pass->shader->getShaderConstHandle( "$viewToObj" ); pass->eyePosWorldConst = pass->shader->getShaderConstHandle( "$eyePosWorld" ); pass->eyePosConst = pass->shader->getShaderConstHandle( "$eyePos" ); pass->vEyeConst = pass->shader->getShaderConstHandle( "$vEye" ); pass->layerSizeConst = pass->shader->getShaderConstHandle( "$layerSize" ); pass->objTransConst = pass->shader->getShaderConstHandle( "$objTrans" ); pass->worldToObjConst = pass->shader->getShaderConstHandle( "$worldToObj" ); pass->lightInfoBufferConst = pass->shader->getShaderConstHandle( "$lightInfoBuffer" ); pass->baseTexMapConst = pass->shader->getShaderConstHandle( "$baseTexMap" ); pass->layerTexConst = pass->shader->getShaderConstHandle( "$layerTex" ); pass->fogDataConst = pass->shader->getShaderConstHandle( "$fogData" ); pass->fogColorConst = pass->shader->getShaderConstHandle( "$fogColor" ); pass->lightMapTexConst = pass->shader->getShaderConstHandle( "$lightMapTex" ); pass->oneOverTerrainSize = pass->shader->getShaderConstHandle( "$oneOverTerrainSize" ); pass->squareSize = pass->shader->getShaderConstHandle( "$squareSize" ); pass->lightParamsConst = pass->shader->getShaderConstHandle( "$rtParamslightInfoBuffer" ); // Now prepare the basic stateblock. GFXStateBlockDesc desc; if ( !firstPass ) { desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha ); // If this is the prepass then we don't want to // write to the last two color channels (where // depth is usually encoded). // // This trick works in combination with the // MFT_TerrainAdditive feature to lerp the // output normal with the previous pass. // if ( prePassMat ) desc.setColorWrites( true, true, true, false ); } // We write to the zbuffer if this is a prepass // material or if the prepass is disabled. desc.setZReadWrite( true, !MATMGR->getPrePassEnabled() || prePassMat || reflectMat ); desc.samplersDefined = true; if ( pass->baseTexMapConst->isValid() ) desc.samplers[pass->baseTexMapConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear(); if ( pass->layerTexConst->isValid() ) desc.samplers[pass->layerTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint(); if ( pass->lightInfoBufferConst->isValid() ) desc.samplers[pass->lightInfoBufferConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint(); if ( pass->lightMapTexConst->isValid() ) desc.samplers[pass->lightMapTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear(); const U32 maxAnisotropy = MATMGR->getDefaultAnisotropy(); // Finally setup the material specific shader // constants and stateblock state. // // NOTE: If this changes be sure to check TerrainCellMaterial::_updateDefaultAnisotropy // to see if it needs the same changes. // for ( U32 i=0; i < pass->materials.size(); i++ ) { MaterialInfo *matInfo = pass->materials[i]; matInfo->detailInfoVConst = pass->shader->getShaderConstHandle( avar( "$detailScaleAndFade%d", i ) ); matInfo->detailInfoPConst = pass->shader->getShaderConstHandle( avar( "$detailIdStrengthParallax%d", i ) ); matInfo->detailTexConst = pass->shader->getShaderConstHandle( avar( "$detailMap%d", i ) ); if ( matInfo->detailTexConst->isValid() ) { const S32 sampler = matInfo->detailTexConst->getSamplerRegister(); desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); desc.samplers[sampler].magFilter = GFXTextureFilterLinear; desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; if ( maxAnisotropy > 1 ) { desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; desc.samplers[sampler].maxAnisotropy = maxAnisotropy; } else desc.samplers[sampler].minFilter = GFXTextureFilterLinear; matInfo->detailTex.set( matInfo->mat->getDetailMap(), &GFXDefaultStaticDiffuseProfile, "TerrainCellMaterial::_createPass() - DetailMap" ); } matInfo->macroInfoVConst = pass->shader->getShaderConstHandle( avar( "$macroScaleAndFade%d", i ) ); matInfo->macroInfoPConst = pass->shader->getShaderConstHandle( avar( "$macroIdStrengthParallax%d", i ) ); matInfo->macroTexConst = pass->shader->getShaderConstHandle( avar( "$macroMap%d", i ) ); if ( matInfo->macroTexConst->isValid() ) { const S32 sampler = matInfo->macroTexConst->getSamplerRegister(); desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); desc.samplers[sampler].magFilter = GFXTextureFilterLinear; desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; if ( maxAnisotropy > 1 ) { desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; desc.samplers[sampler].maxAnisotropy = maxAnisotropy; } else desc.samplers[sampler].minFilter = GFXTextureFilterLinear; matInfo->macroTex.set( matInfo->mat->getMacroMap(), &GFXDefaultStaticDiffuseProfile, "TerrainCellMaterial::_createPass() - MacroMap" ); } //end macro texture matInfo->normalTexConst = pass->shader->getShaderConstHandle( avar( "$normalMap%d", i ) ); if ( matInfo->normalTexConst->isValid() ) { const S32 sampler = matInfo->normalTexConst->getSamplerRegister(); desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); desc.samplers[sampler].magFilter = GFXTextureFilterLinear; desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; if ( maxAnisotropy > 1 ) { desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; desc.samplers[sampler].maxAnisotropy = maxAnisotropy; } else desc.samplers[sampler].minFilter = GFXTextureFilterLinear; matInfo->normalTex = normalMaps[i]; } } // Remove the materials we processed and leave the // ones that remain for the next pass. for ( U32 i=0; i < matCount; i++ ) { MaterialInfo *matInfo = materials->first(); if ( baseOnly || pass->materials.find_next( matInfo ) == -1 ) delete matInfo; materials->pop_front(); } // If we're doing prepass it requires some // special stencil settings for it to work. if ( prePassMat ) desc.addDesc( RenderPrePassMgr::getOpaqueStenciWriteDesc( false ) ); desc.setCullMode( GFXCullCCW ); pass->stateBlock = GFX->createStateBlock(desc); //reflection stateblock desc.setCullMode( GFXCullCW ); pass->reflectionStateBlock = GFX->createStateBlock(desc); // Create the wireframe state blocks. GFXStateBlockDesc wireframe( desc ); wireframe.fillMode = GFXFillWireframe; wireframe.setCullMode( GFXCullCCW ); pass->wireframeStateBlock = GFX->createStateBlock( wireframe ); return true; }