How to change shadow nodes on the fly

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


aymar
Greenskin
Posts: 145
Joined: Fri Jun 12, 2015 6:53 pm
Location: Florianopolis, Brazil
x 17

How to change shadow nodes on the fly

Post by aymar »

Hey guys,

I want to setup multiple choices of shadow quality in my game, and at least my initial idea was to setup multiple shadownodes and then change the working one on the fly.
How do I do that, I couldn't find where to set the shadow node in a CompositorPass or CompositorPassDef, and more importantly, is that the best idea to have multiple shadow quality choices in my game?

Cheers.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5478
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1359

Re: How to change shadow nodes on the fly

Post by dark_sylinc »

Except for PCF filtering, you'll need to destroy the workspace and either:
  • Programmatically destroy the shadow node, and it again, suiting the new setting
  • Predefine a number of shadow nodes for each quality, and programatically change the nodes using these shadow nodes so they reference the other shadow node (e.g. if they referenced "ShadowNodeHigh" they now reference "ShadowNodeLow")
Then recreate those workspaces.

We personally went for the first method, for reference this is what we do (note that it does some assumptions; you may have to suit it for your needs):

Code: Select all

void setShadowMapping( bool shadowsEnabled, uint32 numShadowCastingLights,
                                              uint32 resolution = 2048,
                                              bool usePssm = true, uint32 numPssmSplits = 3,
                                              HlmsPbs::ShadowFilter shadowFilter = HlmsPbs::PCF_3x3 )
{
    size_t numShadowMaps = numShadowCastingLights + (usePssm ? (numPssmSplits - 1) : 0);
    size_t split = 0;
    size_t lightIdx = 0;

    CompositorManager2 *compositorManager = Root::getSingleton().getCompositorManager2();
    compositorManager->removeAllShadowNodeDefinitions();

    CompositorShadowNodeDef *shadowNodeDef = compositorManager->addShadowNodeDefinition( "myShadowNode" );

    shadowNodeDef->setNumLocalTextureDefinitions( numShadowMaps );

    shadowNodeDef->setDefaultTechnique( SHADOWMAP_PSSM );

    for( size_t i=0; i<numShadowMaps; ++i )
    {
        ShadowTextureDefinition *shadowTexDef =
                shadowNodeDef->addShadowTextureDefinition( lightIdx, split,
                                                           StringConverter::toString(i),
                                                           false );

        shadowTexDef->width         = resolution;
        shadowTexDef->height        = resolution;
        shadowTexDef->formatList.push_back( PF_D32_FLOAT );
        shadowTexDef->depthBufferId = DepthBuffer::POOL_NON_SHAREABLE;

        if( usePssm && i < numPssmSplits )
        {
            shadowTexDef->shadowMapTechnique    = SHADOWMAP_PSSM;
            shadowTexDef->pssmLambda            = 0.95f;
            shadowTexDef->numSplits             = 3;
            ++split;

            if( split >= numPssmSplits )
            {
                split = 0;
                ++lightIdx;
            }
        }
        else
        {
            shadowNodeDef->setDefaultTechnique( SHADOWMAP_FOCUSED );
            shadowTexDef->shadowMapTechnique    = SHADOWMAP_FOCUSED;
            split = 0;
            ++lightIdx;
        }
    }

    shadowNodeDef->setNumTargetPass( numShadowMaps );

    for( size_t i=0; i<numShadowMaps; ++i )
    {
        CompositorTargetDef *targetPassDef = shadowNodeDef->addTargetPass( StringConverter::toString(i) );
        targetPassDef->setNumPasses( 2 );

        {
            CompositorPassClearDef *pass = static_cast<CompositorPassClearDef*>(
                                                targetPassDef->addPass( PASS_CLEAR ) );
            pass->mClearBufferFlags = FBT_DEPTH|FBT_STENCIL;
            pass->mShadowMapIdx     = i;
            pass->mIncludeOverlays  = false;
        }

        {
            CompositorPassSceneDef *pass = static_cast<CompositorPassSceneDef*>(
                                                targetPassDef->addPass( PASS_SCENE ) );
            pass->mVisibilityMask   = 0x00000001;
            pass->mShadowMapIdx     = i;
            pass->mIncludeOverlays  = false;
        }
    }

    CompositorNodeDef *nodeDef = compositorManager->getNodeDefinitionNonConst( "DefaultNode" );

    for( size_t i=0; i<nodeDef->getNumTargetPasses(); ++i )
    {
        CompositorTargetDef *targetDef = nodeDef->getTargetPass( i );

        const CompositorPassDefVec &passDefs = targetDef->getCompositorPasses();
        CompositorPassDefVec::const_iterator itor = passDefs.begin();
        CompositorPassDefVec::const_iterator end  = passDefs.end();

        while( itor != end )
        {
            CompositorPassDef *passDef = *itor;

            if( passDef->mIdentifier == 1000 ) //We actually flag which passes use a shadow node with an identifier
            {
                assert( passDef->getType() == PASS_SCENE );
                CompositorPassSceneDef *passSceneDef = static_cast<CompositorPassSceneDef*>(passDef);
                passSceneDef->mShadowNode = shadowsEnabled ? IdString( "myShadowNode" ) : IdString();
            }
            else
            {
                assert( passDef->getType() != PASS_SCENE );
            }

            ++itor;
        }
    }

    Ogre::Root *root = Root::getSingletonPtr();
    Ogre::HlmsManager *hlmsManager = root->getHlmsManager();
    Ogre::Hlms *hlms = hlmsManager->getHlms( Ogre::HLMS_PBS );

    Ogre::HlmsPbs *hlmsPbs = static_cast<Ogre::HlmsPbs*>(hlms);
    hlmsPbs->setShadowSettings( shadowFilter );
}
Note that you should destroy all workspaces using that shadow node before calling setShadowMapping, then create them again. If you forgot to destroy one of those workspaces; you'll have a workspace with a dangling pointer as Shadow Node and will crash.

If you go for the 2nd option (predefined quality nodes); then you'll only need the last part where "passSceneDef->mShadowNode" is set (and set it to the predefined node you want). Though you will still have to recreate the instances.
aymar
Greenskin
Posts: 145
Joined: Fri Jun 12, 2015 6:53 pm
Location: Florianopolis, Brazil
x 17

Re: How to change shadow nodes on the fly

Post by aymar »

Great, it worked!

I did the second method, and setup a few pre defined shadow nodes, then the code I used to swap between them was the following:
Ogre::CompositorManager2* compositorManager = Ogre::Root::getSingleton().getCompositorManager2();
Ogre::CompositorNodeDef* nodeDef = compositorManager->getNodeDefinitionNonConst("ClientNode");
for (int i = 0; i < nodeDef->getNumTargetPasses(); ++i) {
Ogre::CompositorTargetDef *targetDef = nodeDef->getTargetPass(i);

const Ogre::CompositorPassDefVec &passDefs = targetDef->getCompositorPasses();
Ogre::CompositorPassDefVec::const_iterator itor = passDefs.begin();
Ogre::CompositorPassDefVec::const_iterator end = passDefs.end();

while (itor != end) {
Ogre::CompositorPassDef *passDef = *itor;
if (passDef->getType() == Ogre::PASS_SCENE) {
Ogre::CompositorPassSceneDef* passSceneDef = static_cast<Ogre::CompositorPassSceneDef*>(passDef);
if (passSceneDef->mShadowNode.mHash != 0) {
passSceneDef->mShadowNode = Ogre::IdString(shadowNode);
}
}
++itor;
}
}
Ogre::CompositorWorkspace* workspace = Core::getInstance()->getGraphicsSystem()->getWorkspace("ClientWorkspace");
workspace->recreateAllNodes();
workspace->reconnectAllNodes();
Thanks.