//----------------------------------------------------------------------------- // 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 "renderInstance/renderDeferredMgr.h" #include "gfx/gfxTransformSaver.h" #include "materials/sceneData.h" #include "materials/materialManager.h" #include "materials/materialFeatureTypes.h" #include "core/util/safeDelete.h" #include "shaderGen/featureMgr.h" #include "shaderGen/HLSL/depthHLSL.h" #include "shaderGen/GLSL/depthGLSL.h" #include "shaderGen/conditionerFeature.h" #include "shaderGen/shaderGenVars.h" #include "scene/sceneRenderState.h" #include "gfx/gfxStringEnumTranslate.h" #include "gfx/gfxDebugEvent.h" #include "gfx/gfxCardProfile.h" #include "materials/customMaterialDefinition.h" #include "lighting/advanced/advancedLightManager.h" #include "lighting/advanced/advancedLightBinManager.h" #include "terrain/terrCell.h" #include "renderInstance/renderTerrainMgr.h" #include "terrain/terrCellMaterial.h" #include "math/mathUtils.h" #include "math/util/matrixSet.h" #include "gfx/gfxTextureManager.h" #include "gfx/primBuilder.h" #include "gfx/gfxDrawUtil.h" #include "materials/shaderData.h" #include "gfx/sim/cubemapData.h" #include "materials/customShaderBindingData.h" const MatInstanceHookType DeferredMatInstanceHook::Type( "Deferred" ); const String RenderDeferredMgr::BufferName("deferred"); const RenderInstType RenderDeferredMgr::RIT_Deferred("Deferred"); const String RenderDeferredMgr::ColorBufferName("color"); const String RenderDeferredMgr::MatInfoBufferName("matinfo"); IMPLEMENT_CONOBJECT(RenderDeferredMgr); ConsoleDocClass( RenderDeferredMgr, "@brief The render bin which performs a z+normals deferred used in Advanced Lighting.\n\n" "This render bin is used in Advanced Lighting to gather all opaque mesh render instances " "and render them to the g-buffer for use in lighting the scene and doing effects.\n\n" "PostEffect and other shaders can access the output of this bin by using the #deferred " "texture target name. See the edge anti-aliasing post effect for an example.\n\n" "@see game/core/scripts/client/postFx/edgeAA." TORQUE_SCRIPT_EXTENSION "\n" "@ingroup RenderBin\n" ); RenderDeferredMgr::RenderSignal& RenderDeferredMgr::getRenderSignal() { static RenderSignal theSignal; return theSignal; } RenderDeferredMgr::RenderDeferredMgr( bool gatherDepth, GFXFormat format ) : Parent( RIT_Deferred, 0.01f, 0.01f, format, Point2I( Parent::DefaultTargetSize, Parent::DefaultTargetSize), gatherDepth ? Parent::DefaultTargetChainLength : 0 ), mDeferredMatInstance( NULL ) { notifyType( RenderPassManager::RIT_Decal ); notifyType( RenderPassManager::RIT_DecalRoad ); notifyType( RenderPassManager::RIT_Mesh ); notifyType( RenderPassManager::RIT_Terrain ); notifyType( RenderPassManager::RIT_Object ); notifyType( RenderPassManager::RIT_Probes ); // We want a full-resolution buffer mTargetSizeType = RenderTexTargetBinManager::WindowSize; if(getTargetChainLength() > 0) GFXShader::addGlobalMacro( "TORQUE_LINEAR_DEPTH" ); mNamedTarget.registerWithName( BufferName ); mColorTarget.registerWithName( ColorBufferName ); mMatInfoTarget.registerWithName( MatInfoBufferName ); _registerFeatures(); } RenderDeferredMgr::~RenderDeferredMgr() { GFXShader::removeGlobalMacro( "TORQUE_LINEAR_DEPTH" ); mColorTarget.release(); mMatInfoTarget.release(); _unregisterFeatures(); SAFE_DELETE( mDeferredMatInstance ); } void RenderDeferredMgr::_registerFeatures() { ConditionerFeature *cond = new LinearEyeDepthConditioner( getTargetFormat() ); FEATUREMGR->registerFeature( MFT_DeferredConditioner, cond ); mNamedTarget.setConditioner( cond ); } void RenderDeferredMgr::_unregisterFeatures() { mNamedTarget.setConditioner( NULL ); FEATUREMGR->unregisterFeature(MFT_DeferredConditioner); } bool RenderDeferredMgr::setTargetSize(const Point2I &newTargetSize) { bool ret = Parent::setTargetSize( newTargetSize ); mNamedTarget.setViewport( GFX->getViewport() ); mColorTarget.setViewport( GFX->getViewport() ); mMatInfoTarget.setViewport( GFX->getViewport() ); return ret; } bool RenderDeferredMgr::_updateTargets() { PROFILE_SCOPE(RenderDeferredMgr_updateTargets); bool ret = Parent::_updateTargets(); // check for an output conditioner, and update it's format ConditionerFeature *outputConditioner = dynamic_cast(FEATUREMGR->getByType(MFT_DeferredConditioner)); if( outputConditioner && outputConditioner->setBufferFormat(mTargetFormat) ) { // reload materials, the conditioner needs to alter the generated shaders } // TODO: these formats should be passed in and not hard-coded const GFXFormat colorFormat = GFXFormatR8G8B8A8_SRGB; const GFXFormat matInfoFormat = GFXFormatR8G8B8A8; // andrewmac: Deferred Shading Color Buffer if (mColorTex.getFormat() != colorFormat || mColorTex.getWidthHeight() != mTargetSize || GFX->recentlyReset()) { mColorTarget.release(); mColorTex.set(mTargetSize.x, mTargetSize.y, colorFormat, &GFXRenderTargetSRGBProfile, avar("%s() - (line %d)", __FUNCTION__, __LINE__), 1, GFXTextureManager::AA_MATCH_BACKBUFFER); mColorTarget.setTexture(mColorTex); for (U32 i = 0; i < mTargetChainLength; i++) mTargetChain[i]->attachTexture(GFXTextureTarget::Color1, mColorTarget.getTexture()); } // andrewmac: Deferred Shading Material Info Buffer if (mMatInfoTex.getFormat() != matInfoFormat || mMatInfoTex.getWidthHeight() != mTargetSize || GFX->recentlyReset()) { mMatInfoTarget.release(); mMatInfoTex.set(mTargetSize.x, mTargetSize.y, matInfoFormat, &GFXRenderTargetProfile, avar("%s() - (line %d)", __FUNCTION__, __LINE__), 1, GFXTextureManager::AA_MATCH_BACKBUFFER); mMatInfoTarget.setTexture(mMatInfoTex); for (U32 i = 0; i < mTargetChainLength; i++) mTargetChain[i]->attachTexture(GFXTextureTarget::Color2, mMatInfoTarget.getTexture()); } //scene color target NamedTexTargetRef sceneColorTargetRef = NamedTexTarget::find("AL_FormatToken"); if (sceneColorTargetRef.isValid()) { for (U32 i = 0; i < mTargetChainLength; i++) mTargetChain[i]->attachTexture(GFXTextureTarget::Color3, sceneColorTargetRef->getTexture(0)); } else { Con::errorf("RenderDeferredMgr: Could not find AL_FormatToken"); return false; } GFX->finalizeReset(); return ret; } void RenderDeferredMgr::_createDeferredMaterial() { SAFE_DELETE(mDeferredMatInstance); const GFXVertexFormat *vertexFormat = getGFXVertexFormat(); MatInstance* deferredMat = static_cast(MATMGR->createMatInstance("AL_DefaultDeferredMaterial", vertexFormat)); AssertFatal( deferredMat, "TODO: Handle this better." ); mDeferredMatInstance = new DeferredMatInstance(deferredMat, this); mDeferredMatInstance->init( MATMGR->getDefaultFeatures(), vertexFormat); delete deferredMat; } void RenderDeferredMgr::setDeferredMaterial( DeferredMatInstance *mat ) { SAFE_DELETE(mDeferredMatInstance); mDeferredMatInstance = mat; } void RenderDeferredMgr::addElement( RenderInst *inst ) { PROFILE_SCOPE( RenderDeferredMgr_addElement ) // Skip out if this bin is disabled. if ( gClientSceneGraph->getCurrentRenderState() && gClientSceneGraph->getCurrentRenderState()->disableAdvancedLightingBins() ) return; // First what type of render instance is it? const bool isDecalMeshInst = ((inst->type == RenderPassManager::RIT_Decal)||(inst->type == RenderPassManager::RIT_DecalRoad)); const bool isMeshInst = inst->type == RenderPassManager::RIT_Mesh; const bool isTerrainInst = inst->type == RenderPassManager::RIT_Terrain; const bool isProbeInst = inst->type == RenderPassManager::RIT_Probes; // Get the material if its a mesh. BaseMatInstance* matInst = NULL; if ( isMeshInst || isDecalMeshInst ) matInst = static_cast(inst)->matInst; if (matInst) { // If its a custom material and it refracts... skip it. if (matInst->isCustomMaterial() && static_cast(matInst->getMaterial())->mRefract) return; // Make sure we got a deferred material. matInst = getDeferredMaterial(matInst); if (!matInst || !matInst->isValid()) return; } // We're gonna add it to the bin... get the right element list. Vector< MainSortElem > *elementList; if ( isMeshInst || isDecalMeshInst ) elementList = &mElementList; else if ( isTerrainInst ) elementList = &mTerrainElementList; else if (isProbeInst) elementList = &mProbeElementList; else elementList = &mObjectElementList; elementList->increment(); MainSortElem &elem = elementList->last(); elem.inst = inst; // Store the original key... we might need it. U32 originalKey = elem.key; // Sort front-to-back first to get the most fillrate savings. const F32 invSortDistSq = F32_MAX - inst->sortDistSq; elem.key = *((U32*)&invSortDistSq); // Next sort by pre-pass material if its a mesh... use the original sort key. if (isMeshInst && matInst) elem.key2 = matInst->getStateHint(); else elem.key2 = originalKey; } void RenderDeferredMgr::sort() { PROFILE_SCOPE( RenderDeferredMgr_sort ); Parent::sort(); dQsort( mTerrainElementList.address(), mTerrainElementList.size(), sizeof(MainSortElem), cmpKeyFunc); dQsort( mObjectElementList.address(), mObjectElementList.size(), sizeof(MainSortElem), cmpKeyFunc); } void RenderDeferredMgr::clear() { Parent::clear(); mProbeElementList.clear(); mTerrainElementList.clear(); mObjectElementList.clear(); } void RenderDeferredMgr::render( SceneRenderState *state ) { PROFILE_SCOPE(RenderDeferredMgr_render); // Take a look at the SceneRenderState and see if we should skip drawing the pre-pass if ( state->disableAdvancedLightingBins() ) return; // NOTE: We don't early out here when the element list is // zero because we need the deferred to be cleared. // Automagically save & restore our viewport and transforms. GFXTransformSaver saver; GFXDEBUGEVENT_SCOPE( RenderDeferredMgr_Render, ColorI::RED ); // Tell the superclass we're about to render const bool isRenderingToTarget = _onPreRender(state); // Clear z-buffer and g-buffer. GFX->clear(GFXClearZBuffer | GFXClearStencil, LinearColorF::ZERO, 1.0f, 0); GFX->clearColorAttachment(0, LinearColorF::ONE);//normdepth GFX->clearColorAttachment(1, LinearColorF::ZERO);//albedo GFX->clearColorAttachment(2, LinearColorF::ZERO);//matinfo //AL_FormatToken is cleared by it's own class // Restore transforms MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); matrixSet.restoreSceneViewProjection(); const MatrixF worldViewXfm = GFX->getWorldMatrix(); // Setup the default deferred material for object instances. if ( !mDeferredMatInstance ) _createDeferredMaterial(); if ( mDeferredMatInstance ) { matrixSet.setWorld(MatrixF::Identity); mDeferredMatInstance->setTransforms(matrixSet, state); } // Signal start of deferred getRenderSignal().trigger( state, this, true ); // First do a loop and render all the terrain... these are // usually the big blockers in a scene and will save us fillrate // on the smaller meshes and objects. // The terrain doesn't need any scene graph data // in the the deferred... so just clear it. SceneData sgData; sgData.init( state, SceneData::DeferredBin ); Vector< MainSortElem >::const_iterator itr = mTerrainElementList.begin(); for ( ; itr != mTerrainElementList.end(); itr++ ) { TerrainRenderInst *ri = static_cast( itr->inst ); TerrainCellMaterial *mat = ri->cellMat->getDeferredMat(); GFX->setPrimitiveBuffer( ri->primBuff ); GFX->setVertexBuffer( ri->vertBuff ); mat->setTransformAndEye( *ri->objectToWorldXfm, worldViewXfm, GFX->getProjectionMatrix(), state->getFarPlane() ); while ( mat->setupPass( state, sgData ) ) GFX->drawPrimitive( ri->prim ); } // init loop data GFXTextureObject *lastLM = NULL; GFXCubemap *lastCubemap = NULL; GFXTextureObject *lastReflectTex = NULL; GFXTextureObject *lastAccuTex = NULL; // Next render all the meshes. itr = mElementList.begin(); for ( ; itr != mElementList.end(); ) { MeshRenderInst *ri = static_cast( itr->inst ); // Get the deferred material. BaseMatInstance *mat = getDeferredMaterial( ri->matInst ); // Set up SG data proper like and flag it // as a pre-pass render setupSGData( ri, sgData ); Vector< MainSortElem >::const_iterator meshItr, endOfBatchItr = itr; while ( mat->setupPass( state, sgData ) ) { meshItr = itr; for ( ; meshItr != mElementList.end(); meshItr++ ) { MeshRenderInst *passRI = static_cast( meshItr->inst ); // Check to see if we need to break this batch. // // NOTE: We're comparing the non-deferred materials // here so we don't incur the cost of looking up the // deferred hook on each inst. // if ( newPassNeeded( ri, passRI ) ) break; // Set up SG data for this instance. setupSGData( passRI, sgData ); mat->setSceneInfo(state, sgData); matrixSet.setWorld(*passRI->objectToWorld); matrixSet.setView(*passRI->worldToCamera); matrixSet.setProjection(*passRI->projection); mat->setTransforms(matrixSet, state); // Setup HW skinning transforms if applicable if (mat->usesHardwareSkinning()) { mat->setNodeTransforms(passRI->mNodeTransforms, passRI->mNodeTransformCount); } //push along any overriden fields that are instance-specific as well if (passRI->mCustomShaderData.size() > 0) { mat->setCustomShaderData(passRI->mCustomShaderData); } // If we're instanced then don't render yet. if ( mat->isInstanced() ) { // Let the material increment the instance buffer, but // break the batch if it runs out of room for more. if ( !mat->stepInstance() ) { meshItr++; break; } continue; } bool dirty = false; // set the lightmaps if different if( passRI->lightmap && passRI->lightmap != lastLM ) { sgData.lightmap = passRI->lightmap; lastLM = passRI->lightmap; dirty = true; } // set the cubemap if different. if ( passRI->cubemap != lastCubemap ) { sgData.cubemap = passRI->cubemap; lastCubemap = passRI->cubemap; dirty = true; } if ( passRI->reflectTex != lastReflectTex ) { sgData.reflectTex = passRI->reflectTex; lastReflectTex = passRI->reflectTex; dirty = true; } // Update accumulation texture if it changed. // Note: accumulation texture can be NULL, and must be updated. if (passRI->accuTex != lastAccuTex) { sgData.accuTex = passRI->accuTex; lastAccuTex = passRI->accuTex; dirty = true; } if ( dirty ) mat->setTextureStages( state, sgData ); // Setup the vertex and index buffers. mat->setBuffers( passRI->vertBuff, passRI->primBuff ); // Render this sucker. if ( passRI->prim ) GFX->drawPrimitive( *passRI->prim ); else GFX->drawPrimitive( passRI->primBuffIndex ); } // Draw the instanced batch. if ( mat->isInstanced() ) { // Sets the buffers including the instancing stream. mat->setBuffers( ri->vertBuff, ri->primBuff ); if ( ri->prim ) GFX->drawPrimitive( *ri->prim ); else GFX->drawPrimitive( ri->primBuffIndex ); } endOfBatchItr = meshItr; } // while( mat->setupPass(state, sgData) ) // Force the increment if none happened, otherwise go to end of batch. itr = ( itr == endOfBatchItr ) ? itr + 1 : endOfBatchItr; } // The final loop is for object render instances. itr = mObjectElementList.begin(); for ( ; itr != mObjectElementList.end(); itr++ ) { ObjectRenderInst *ri = static_cast( itr->inst ); if ( ri->renderDelegate ) ri->renderDelegate( ri, state, mDeferredMatInstance ); } // Signal end of pre-pass getRenderSignal().trigger( state, this, false ); if(isRenderingToTarget) _onPostRender(); } const GFXStateBlockDesc & RenderDeferredMgr::getOpaqueStenciWriteDesc( bool lightmappedGeometry /*= true*/ ) { static bool sbInit = false; static GFXStateBlockDesc sOpaqueStaticLitStencilWriteDesc; static GFXStateBlockDesc sOpaqueDynamicLitStencilWriteDesc; if(!sbInit) { sbInit = true; // Build the static opaque stencil write/test state block descriptions sOpaqueStaticLitStencilWriteDesc.stencilDefined = true; sOpaqueStaticLitStencilWriteDesc.stencilEnable = true; sOpaqueStaticLitStencilWriteDesc.stencilWriteMask = 0x03; sOpaqueStaticLitStencilWriteDesc.stencilMask = 0x03; sOpaqueStaticLitStencilWriteDesc.stencilRef = RenderDeferredMgr::OpaqueStaticLitMask; sOpaqueStaticLitStencilWriteDesc.stencilPassOp = GFXStencilOpReplace; sOpaqueStaticLitStencilWriteDesc.stencilFailOp = GFXStencilOpKeep; sOpaqueStaticLitStencilWriteDesc.stencilZFailOp = GFXStencilOpKeep; sOpaqueStaticLitStencilWriteDesc.stencilFunc = GFXCmpAlways; // Same only dynamic sOpaqueDynamicLitStencilWriteDesc = sOpaqueStaticLitStencilWriteDesc; sOpaqueDynamicLitStencilWriteDesc.stencilRef = RenderDeferredMgr::OpaqueDynamicLitMask; } return (lightmappedGeometry ? sOpaqueStaticLitStencilWriteDesc : sOpaqueDynamicLitStencilWriteDesc); } const GFXStateBlockDesc & RenderDeferredMgr::getOpaqueStencilTestDesc() { static bool sbInit = false; static GFXStateBlockDesc sOpaqueStencilTestDesc; if(!sbInit) { // Build opaque test sbInit = true; sOpaqueStencilTestDesc.stencilDefined = true; sOpaqueStencilTestDesc.stencilEnable = true; sOpaqueStencilTestDesc.stencilWriteMask = 0xFE; sOpaqueStencilTestDesc.stencilMask = 0x03; sOpaqueStencilTestDesc.stencilRef = 0; sOpaqueStencilTestDesc.stencilPassOp = GFXStencilOpKeep; sOpaqueStencilTestDesc.stencilFailOp = GFXStencilOpKeep; sOpaqueStencilTestDesc.stencilZFailOp = GFXStencilOpKeep; sOpaqueStencilTestDesc.stencilFunc = GFXCmpLess; } return sOpaqueStencilTestDesc; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ ProcessedDeferredMaterial::ProcessedDeferredMaterial( Material& mat, const RenderDeferredMgr *deferredMgr ) : Parent(mat), mDeferredMgr(deferredMgr), mIsLightmappedGeometry(false) { } void ProcessedDeferredMaterial::_determineFeatures( U32 stageNum, MaterialFeatureData &fd, const FeatureSet &features ) { Parent::_determineFeatures( stageNum, fd, features ); if (fd.features.hasFeature(MFT_ForwardShading)) return; // Find this for use down below... bool bEnableMRTLightmap = false; AdvancedLightBinManager *lightBin; if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) bEnableMRTLightmap = lightBin->MRTLightmapsDuringDeferred(); // If this material has a lightmap or tonemap (texture or baked vertex color), // it must be static. Otherwise it is dynamic. mIsLightmappedGeometry = ( fd.features.hasFeature( MFT_ToneMap ) || fd.features.hasFeature( MFT_LightMap ) || fd.features.hasFeature( MFT_VertLit ) || ( bEnableMRTLightmap && (fd.features.hasFeature( MFT_IsTranslucent ) || fd.features.hasFeature( MFT_ForwardShading ) || fd.features.hasFeature( MFT_IsTranslucentZWrite) ) ) ); // Integrate proper opaque stencil write state mUserDefined.addDesc( mDeferredMgr->getOpaqueStenciWriteDesc( mIsLightmappedGeometry ) ); FeatureSet newFeatures; // These are always on for deferred. newFeatures.addFeature( MFT_EyeSpaceDepthOut ); newFeatures.addFeature( MFT_DeferredConditioner ); #ifndef TORQUE_DEDICATED //tag all materials running through deferred as deferred newFeatures.addFeature(MFT_isDeferred); // Deferred Shading : Diffuse if (mStages[stageNum].getTex( MFT_DiffuseMap )) { newFeatures.addFeature(MFT_DiffuseMap); } newFeatures.addFeature( MFT_DiffuseColor ); if (mMaterial->mInvertRoughness[stageNum]) newFeatures.addFeature(MFT_InvertRoughness); // Deferred Shading : PBR Config if( mStages[stageNum].getTex( MFT_OrmMap ) ) { newFeatures.addFeature( MFT_OrmMap ); } else newFeatures.addFeature( MFT_ORMConfigVars ); if (mStages[stageNum].getTex(MFT_GlowMap)) { newFeatures.addFeature(MFT_GlowMap); } // Deferred Shading : Material Info Flags newFeatures.addFeature( MFT_MatInfoFlags ); for ( U32 i=0; i < fd.features.getCount(); i++ ) { const FeatureType &type = fd.features.getAt( i ); // Turn on the diffuse texture only if we // have alpha test. if ( type == MFT_AlphaTest ) { newFeatures.addFeature( MFT_AlphaTest ); newFeatures.addFeature( MFT_DiffuseMap ); } else if ( type == MFT_IsTranslucentZWrite ) { newFeatures.addFeature( MFT_IsTranslucentZWrite ); newFeatures.addFeature( MFT_DiffuseMap ); } // Always allow these. else if ( type == MFT_IsBC3nm || type == MFT_IsBC5nm || type == MFT_TexAnim || type == MFT_NormalMap || type == MFT_DetailNormalMap || type == MFT_AlphaTest || type == MFT_Parallax || type == MFT_Visibility || type == MFT_UseInstancing || type == MFT_DiffuseVertColor || type == MFT_DetailMap || type == MFT_DiffuseMapAtlas) newFeatures.addFeature( type ); // Add any transform features. else if ( type.getGroup() == MFG_PreTransform || type.getGroup() == MFG_Transform || type.getGroup() == MFG_PostTransform ) newFeatures.addFeature( type ); } if (mMaterial->mAccuEnabled[stageNum]) { newFeatures.addFeature(MFT_AccuMap); mHasAccumulation = true; } // we need both diffuse and normal maps + sm3 to have an accu map if (newFeatures[MFT_AccuMap] && (!newFeatures[MFT_DiffuseMap] || !newFeatures[MFT_NormalMap] || GFX->getPixelShaderVersion() < 3.0f)) { AssertWarn(false, "SAHARA: Using an Accu Map requires SM 3.0 and a normal map."); newFeatures.removeFeature(MFT_AccuMap); mHasAccumulation = false; } // if we still have the AccuMap feature, we add all accu constant features if (newFeatures[MFT_AccuMap]) { // add the dependencies of the accu map newFeatures.addFeature(MFT_AccuScale); newFeatures.addFeature(MFT_AccuDirection); newFeatures.addFeature(MFT_AccuStrength); newFeatures.addFeature(MFT_AccuCoverage); newFeatures.addFeature(MFT_AccuSpecular); // now remove some features that are not compatible with this newFeatures.removeFeature(MFT_UseInstancing); } // If there is lightmapped geometry support, add the MRT light buffer features if(bEnableMRTLightmap) { // If this material has a lightmap, pass it through, and flag it to // send it's output to RenderTarget3 if( fd.features.hasFeature( MFT_ToneMap ) ) { newFeatures.addFeature( MFT_ToneMap ); newFeatures.addFeature( MFT_LightbufferMRT ); } else if( fd.features.hasFeature( MFT_LightMap ) ) { newFeatures.addFeature( MFT_LightMap ); newFeatures.addFeature( MFT_LightbufferMRT ); } else if( fd.features.hasFeature( MFT_VertLit ) ) { // Flag un-tone-map if necesasary if( fd.features.hasFeature( MFT_DiffuseMap ) ) newFeatures.addFeature( MFT_VertLitTone ); newFeatures.addFeature( MFT_VertLit ); newFeatures.addFeature( MFT_LightbufferMRT ); } else if (!fd.features.hasFeature(MFT_GlowMap)) { newFeatures.addFeature( MFT_RenderTarget3_Zero ); } } // cubemaps only available on stage 0 for now - bramage if ( stageNum < 1 && ( ( mMaterial->mCubemapData && mMaterial->mCubemapData->mCubemap ) || mMaterial->mDynamicCubemap ) ) { if (!mMaterial->mDynamicCubemap) fd.features.addFeature(MFT_StaticCubemap); newFeatures.addFeature( MFT_CubeMap ); } if (mMaterial->mVertLit[stageNum]) newFeatures.addFeature(MFT_VertLit); if (mMaterial->mMinnaertConstant[stageNum] > 0.0f) newFeatures.addFeature(MFT_MinnaertShading); if (mMaterial->mSubSurface[stageNum]) newFeatures.addFeature(MFT_SubSurface); #endif // Set the new features. fd.features = newFeatures; } U32 ProcessedDeferredMaterial::getNumStages() { // Loops through all stages to determine how many // stages we actually use. // // The first stage is always active else we shouldn't be // creating the material to begin with. U32 numStages = 1; U32 i; for( i=1; imCubemapData || mMaterial->mDynamicCubemap ) { numStages++; continue; } } // If we have a texture for the a feature the // stage is active. if ( mStages[i].hasValidTex() ) stageActive = true; // If this stage has diffuse color, it's active if ( mMaterial->mDiffuse[i].alpha > 0 && mMaterial->mDiffuse[i] != LinearColorF::WHITE ) stageActive = true; // If we have a Material that is vertex lit // then it may not have a texture if( mMaterial->mVertLit[i] ) stageActive = true; // Increment the number of active stages numStages += stageActive; } return numStages; } void ProcessedDeferredMaterial::addStateBlockDesc(const GFXStateBlockDesc& desc) { GFXStateBlockDesc deferredStateBlock = desc; // Adjust color writes if this is a pure z-fill pass const bool pixelOutEnabled = mDeferredMgr->getTargetChainLength() > 0; if ( !pixelOutEnabled ) { deferredStateBlock.colorWriteDefined = true; deferredStateBlock.colorWriteRed = pixelOutEnabled; deferredStateBlock.colorWriteGreen = pixelOutEnabled; deferredStateBlock.colorWriteBlue = pixelOutEnabled; deferredStateBlock.colorWriteAlpha = pixelOutEnabled; } // Never allow the alpha test state when rendering // the deferred as we use the alpha channel for the // depth information... MFT_AlphaTest will handle it. deferredStateBlock.alphaDefined = true; deferredStateBlock.alphaTestEnable = false; // If we're translucent then we're doing deferred blending // which never writes to the depth channels. const bool isTranslucent = getMaterial()->isTranslucent(); if ( isTranslucent ) { deferredStateBlock.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha ); deferredStateBlock.setColorWrites(false, false, false, true); } // Enable z reads, but only enable zwrites if we're not translucent. deferredStateBlock.setZReadWrite( true, isTranslucent ? false : true ); // Pass to parent Parent::addStateBlockDesc(deferredStateBlock); } DeferredMatInstance::DeferredMatInstance(MatInstance* root, const RenderDeferredMgr *deferredMgr) : Parent(*root->getMaterial()), mDeferredMgr(deferredMgr) { mFeatureList = root->getRequestedFeatures(); mVertexFormat = root->getVertexFormat(); mUserObject = root->getUserObject(); } DeferredMatInstance::~DeferredMatInstance() { } ProcessedMaterial* DeferredMatInstance::getShaderMaterial() { return new ProcessedDeferredMaterial(*mMaterial, mDeferredMgr); } bool DeferredMatInstance::init( const FeatureSet &features, const GFXVertexFormat *vertexFormat ) { bool vaild = Parent::init(features, vertexFormat); if (mMaterial && mMaterial->getDiffuseMap(0) != StringTable->EmptyString() && String(mMaterial->getDiffuseMap(0)).startsWith("#")) { String difName = mMaterial->getDiffuseMap(0); String texTargetBufferName = difName.substr(1, difName.length() - 1); NamedTexTarget *texTarget = NamedTexTarget::find(texTargetBufferName); RenderPassData* rpd = getPass(0); if (rpd) { rpd->mTexSlot[0].texTarget = texTarget; rpd->mTexType[0] = Material::TexTarget; rpd->mSamplerNames[0] = "diffuseMap"; } } return vaild; } DeferredMatInstanceHook::DeferredMatInstanceHook( MatInstance *baseMatInst, const RenderDeferredMgr *deferredMgr ) : mHookedDeferredMatInst(NULL), mDeferredManager(deferredMgr) { // If the material is a custom material then // hope that using DefaultDeferredMaterial gives // them a good deferred. if ( baseMatInst->isCustomMaterial() ) { MatInstance* dummyInst = static_cast( MATMGR->createMatInstance( "AL_DefaultDeferredMaterial", baseMatInst->getVertexFormat() ) ); mHookedDeferredMatInst = new DeferredMatInstance( dummyInst, deferredMgr ); mHookedDeferredMatInst->init( dummyInst->getRequestedFeatures(), baseMatInst->getVertexFormat()); delete dummyInst; return; } // Create the deferred material instance. mHookedDeferredMatInst = new DeferredMatInstance(baseMatInst, deferredMgr); mHookedDeferredMatInst->getFeaturesDelegate() = baseMatInst->getFeaturesDelegate(); // Get the features, but remove the instancing feature if the // original material didn't end up using it. FeatureSet features = baseMatInst->getRequestedFeatures(); if ( !baseMatInst->isInstanced() ) features.removeFeature( MFT_UseInstancing ); // Initialize the material. mHookedDeferredMatInst->init(features, baseMatInst->getVertexFormat()); } DeferredMatInstanceHook::~DeferredMatInstanceHook() { SAFE_DELETE(mHookedDeferredMatInst); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void LinearEyeDepthConditioner::processPix( Vector &componentList, const MaterialFeatureData &fd ) { // find depth ShaderFeature *depthFeat = FEATUREMGR->getByType( MFT_EyeSpaceDepthOut ); AssertFatal( depthFeat != NULL, "No eye space depth feature found!" ); Var *depth = (Var*) LangElement::find(depthFeat->getOutputVarName()); AssertFatal( depth, "Something went bad with ShaderGen. The depth should be already generated by the EyeSpaceDepthOut feature." ); MultiLine *meta = new MultiLine; meta->addStatement( assignOutput( depth ) ); output = meta; } Var *LinearEyeDepthConditioner::_conditionOutput( Var *unconditionedOutput, MultiLine *meta ) { Var *retVar = NULL; String fracMethodName = (GFX->getAdapterType() == OpenGL) ? "fract" : "frac"; switch(getBufferFormat()) { case GFXFormatR8G8B8A8: retVar = new Var; retVar->setType("float4"); retVar->setName("_ppDepth"); meta->addStatement( new GenOp( " // depth conditioner: packing to rgba\r\n" ) ); meta->addStatement( new GenOp( avar( " @ = %s(@ * (255.0/256) * float4(1, 255, 255 * 255, 255 * 255 * 255));\r\n", fracMethodName.c_str() ), new DecOp(retVar), unconditionedOutput ) ); break; default: retVar = unconditionedOutput; meta->addStatement( new GenOp( " // depth conditioner: no conditioning\r\n" ) ); break; } AssertFatal( retVar != NULL, avar( "Cannot condition output to buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) ); return retVar; } Var *LinearEyeDepthConditioner::_unconditionInput( Var *conditionedInput, MultiLine *meta ) { String float4Typename = (GFX->getAdapterType() == OpenGL) ? "vec4" : "float4"; Var *retVar = conditionedInput; if(getBufferFormat() != GFXFormat_COUNT) { retVar = new Var; retVar->setType(float4Typename.c_str()); retVar->setName("_ppDepth"); meta->addStatement( new GenOp( avar( " @ = %s(0, 0, 1, 1);\r\n", float4Typename.c_str() ), new DecOp(retVar) ) ); switch(getBufferFormat()) { case GFXFormatR32F: case GFXFormatR16F: meta->addStatement( new GenOp( " // depth conditioner: float texture\r\n" ) ); meta->addStatement( new GenOp( " @.w = @.r;\r\n", retVar, conditionedInput ) ); break; case GFXFormatR8G8B8A8: meta->addStatement( new GenOp( " // depth conditioner: unpacking from rgba\r\n" ) ); meta->addStatement( new GenOp( avar( " @.w = dot(@ * (256.0/255), %s(1, 1 / 255, 1 / (255 * 255), 1 / (255 * 255 * 255)));\r\n", float4Typename.c_str() ) , retVar, conditionedInput ) ); break; default: AssertFatal(false, "LinearEyeDepthConditioner::_unconditionInput - Unrecognized buffer format"); } } return retVar; } Var* LinearEyeDepthConditioner::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ) { const bool isCondition = ( methodType == ConditionerFeature::ConditionMethod ); Var *retVal = NULL; // The uncondition method inputs are changed if( isCondition ) retVal = Parent::printMethodHeader( methodType, methodName, stream, meta ); else { Var *methodVar = new Var; methodVar->setName(methodName); if (GFX->getAdapterType() == OpenGL) methodVar->setType("vec4"); else methodVar->setType("inline float4"); DecOp *methodDecl = new DecOp(methodVar); Var *deferredSampler = new Var; deferredSampler->setName("deferredSamplerVar"); deferredSampler->setType("sampler2D"); DecOp *deferredSamplerDecl = NULL; Var *deferredTex = NULL; DecOp *deferredTexDecl = NULL; if (GFX->getAdapterType() == Direct3D11) { deferredSampler->setType("SamplerState"); deferredTex = new Var; deferredTex->setName("deferredTexVar"); deferredTex->setType("Texture2D"); deferredTexDecl = new DecOp(deferredTex); } deferredSamplerDecl = new DecOp(deferredSampler); Var *screenUV = new Var; screenUV->setName("screenUVVar"); if (GFX->getAdapterType() == OpenGL) screenUV->setType("vec2"); else screenUV->setType("float2"); DecOp *screenUVDecl = new DecOp(screenUV); Var *bufferSample = new Var; bufferSample->setName("bufferSample"); if (GFX->getAdapterType() == OpenGL) bufferSample->setType("vec4"); else bufferSample->setType("float4"); DecOp *bufferSampleDecl = new DecOp(bufferSample); if (deferredTex) meta->addStatement(new GenOp("@(@, @, @)\r\n", methodDecl, deferredSamplerDecl, deferredTexDecl, screenUVDecl)); else meta->addStatement(new GenOp("@(@, @)\r\n", methodDecl, deferredSamplerDecl, screenUVDecl)); meta->addStatement(new GenOp("{\r\n")); meta->addStatement(new GenOp(" // Sampler g-buffer\r\n")); // The linear depth target has no mipmaps, so use tex2dlod when // possible so that the shader compiler can optimize. if (GFX->getAdapterType() == OpenGL) meta->addStatement(new GenOp("@ = texture2DLod(@, @, 0); \r\n", bufferSampleDecl, deferredSampler, screenUV)); else { if (deferredTex) meta->addStatement(new GenOp("@ = @.SampleLevel(@, @, 0);\r\n", bufferSampleDecl, deferredTex, deferredSampler, screenUV)); else meta->addStatement(new GenOp("@ = tex2Dlod(@, float4(@,0,0));\r\n", bufferSampleDecl, deferredSampler, screenUV)); } // We don't use this way of passing var's around, so this should cause a crash // if something uses this improperly retVal = bufferSample; } return retVal; }