void TrapezoidalShadowMapEngine::handleSpotLightEnter(
    SpotLight *spotL, RenderAction *ract, TSMEngineData *data)
{
    RenderPartition *parentPart = ract->getActivePartition();

    Matrixr matEyeToWorld(parentPart->getCameraToWorld());
    Matrixr matWorldToLight;
    Matrixr matEyeToLight;

    Inherited::calcSpotLightMatrices(matWorldToLight, matEyeToLight,
                                     spotL,           matEyeToWorld );

    Matrixr matLightProj;
    Matrixr matLightFull(matWorldToLight);

    Real    shadowNear = (getShadowNear() != 0.f ? 
                          getShadowNear()       : 
                          parentPart->getNear()  );
    Real    shadowFar  = (getShadowFar () != 0.f ?
                          getShadowFar ()       :
                          parentPart->getFar()   );

    Inherited::calcPointLightRange(
        spotL, 0.01f,
        shadowNear, shadowFar, shadowNear, shadowFar);

    MatrixPerspective(matLightProj,
                      spotL->getSpotCutOff(), 1.f,
                      shadowNear, shadowFar       );

    matLightFull.multLeft(matLightProj);

    Inherited::updateShadowTexImage  (data);
    Inherited::updateShadowTexBuffers(data);
    Inherited::updateRenderTargets   (data);

    const FrustumVolume &eyeFrust   = parentPart->getFrustum();
          FrustumVolume  lightFrust;
          Matrixr        matNT;

    lightFrust.setPlanes(matLightFull);

    bool matNTValid = calcTrapezoidalTransform(matNT,
                                               matEyeToWorld, matLightFull,
                                               eyeFrust,      lightFrust   );

    if(matNTValid == false)
        return;

//    Real           cosSpotCutOff = osgCos(spotL->getSpotCutOff());

    Int32          shadowTexUnit = (this->getForceTextureUnit() >= 0) ?
                                    this->getForceTextureUnit()       : 7;
    ShaderProgram *shadowFP      = this->getShadowFragmentProgram();

    if(shadowFP == NULL)
    {
        ShaderProgramUnrecPtr newShadowFP = ShaderProgram::createLocal();
        newShadowFP->setShaderType(GL_FRAGMENT_SHADER);
        newShadowFP->setProgram   (_spotFPCode       );

        newShadowFP->addUniformVariable("TSME_matEyeToLight", matEyeToLight);
        newShadowFP->addUniformVariable("TSME_matLightProj",  matLightProj );
        newShadowFP->addUniformVariable("TSME_matNT",         matNT        );
        newShadowFP->addUniformVariable("TSME_texShadow",     shadowTexUnit);
        newShadowFP->addUniformVariable("TSME_texShadowSizeInv",
                                        Vec2f(1.f / getWidth (), 
                                              1.f / getHeight() )          );

        this->setShadowFragmentProgram(newShadowFP);
        shadowFP = newShadowFP;
    }
    else
    {
        shadowFP->updateUniformVariable("TSME_matEyeToLight", matEyeToLight);
        shadowFP->updateUniformVariable("TSME_matLightProj",  matLightProj );
        shadowFP->updateUniformVariable("TSME_matNT",         matNT        );
    }

    updateLightPassMaterial(data, 0, matNT);

    commitChanges();
    
    this->pushPartition(ract);
    {
        RenderPartition   *part   = ract->getActivePartition( );
        Window            *win    = ract->getWindow         ( );
        FrameBufferObject *target = data->getRenderTargets  (0);
        Background        *back   = data->getBackground     ( );

        part->setRenderTarget(target);
        part->setWindow      (win   );

        part->calcViewportDimension(0.f, 0.f, 1.f, 1.f,
                                    target->getWidth (),
                                    target->getHeight() );

        part->setupProjection(matLightProj, Matrixr::identity());
        part->setupViewing   (matWorldToLight                  );
        
        part->setNear        (parentPart->getNear());
        part->setFar         (parentPart->getFar ());

        part->setFrustum     (lightFrust           );

        part->setBackground  (back                 );

        part->overrideMaterial(data->getLightPassMaterials(0),
                               ract->getActNode           ( ) );

        this->recurseFrom(ract, spotL);
        ract->useNodeList(false      );

        part->overrideMaterial(NULL,
                               ract->getActNode           ( ) );
    }
    this->popPartition(ract);
}
void ShaderShadowMapEngine::handleDirectionalLightEnter(
    DirectionalLight *dirL, RenderAction *ract, SSMEngineData *data)
{
    RenderPartition *parentPart = ract      ->getActivePartition();
    FrustumVolume    camFrust   = parentPart->getFrustum        ();

    Matrix matEyeToWorld  (parentPart->getCameraToWorld());
    Matrix matWorldToLight;
    Matrix matEyeToLight;

    calcDirectionalLightMatrices(matWorldToLight, matEyeToLight,
                                 dirL,            matEyeToWorld );

    // place light camera outside the scene bounding box:
    //  - project camera frustum and scene bounding box into a
    //    coordinate system where the directional light shines
    //    along the -z axis.
    //  - compute 2 AABBs that contain the projected frustum and
    //    scene BB
    //  - width and height of the ortho projection are determined from
    //    the frustum AABB, while near and far are determined by the
    //    scene AABB (offscreen objects cast shadows into the view volume)
          Pnt3f      camVerts  [10];
          Pnt3f      sceneVerts[10];
    const Matrix    &matSceneToWorld = ract->topMatrix ();
          BoxVolume  sceneBB         = ract->getActNode()->getVolume();

    camFrust.getCorners(camVerts  [0], camVerts  [1],
                        camVerts  [2], camVerts  [3],
                        camVerts  [4], camVerts  [5],
                        camVerts  [6], camVerts  [7] );
    sceneBB .getCorners(sceneVerts[0], sceneVerts[1],
                        sceneVerts[2], sceneVerts[3],
                        sceneVerts[4], sceneVerts[5],
                        sceneVerts[6], sceneVerts[7] );

    camVerts  [8].setValues(TypeTraits<Real32>::getMax(),
                            TypeTraits<Real32>::getMax(),
                            TypeTraits<Real32>::getMax() );
    camVerts  [9].setValues(TypeTraits<Real32>::getMin(),
                            TypeTraits<Real32>::getMin(),
                            TypeTraits<Real32>::getMin() );
    sceneVerts[8].setValues(TypeTraits<Real32>::getMax(),
                            TypeTraits<Real32>::getMax(),
                            TypeTraits<Real32>::getMax() );
    sceneVerts[9].setValues(TypeTraits<Real32>::getMin(),
                            TypeTraits<Real32>::getMin(),
                            TypeTraits<Real32>::getMin() );

    for(UInt32 i = 0; i < 8; ++i)
    {
        matWorldToLight.mult(camVerts  [i], camVerts  [i]);

        matSceneToWorld.mult(sceneVerts[i], sceneVerts[i]);
        matWorldToLight.mult(sceneVerts[i], sceneVerts[i]);

        camVerts  [8][0] = osgMin(camVerts  [8][0], camVerts  [i][0]);
        camVerts  [9][0] = osgMax(camVerts  [9][0], camVerts  [i][0]);
        camVerts  [8][1] = osgMin(camVerts  [8][1], camVerts  [i][1]);
        camVerts  [9][1] = osgMax(camVerts  [9][1], camVerts  [i][1]);

        sceneVerts[8][0] = osgMin(sceneVerts[8][0], sceneVerts[i][0]);
        sceneVerts[9][0] = osgMax(sceneVerts[9][0], sceneVerts[i][0]);
        sceneVerts[8][1] = osgMin(sceneVerts[8][1], sceneVerts[i][1]);
        sceneVerts[9][1] = osgMax(sceneVerts[9][1], sceneVerts[i][1]);
        sceneVerts[8][2] = osgMin(sceneVerts[8][2], sceneVerts[i][2]);
        sceneVerts[9][2] = osgMax(sceneVerts[9][2], sceneVerts[i][2]);
    }

    // these points are the corners of the ortho shadow view volume
    Pnt3f lightMin(osgMax(camVerts[8][0], sceneVerts[8][0]),
                   osgMax(camVerts[8][1], sceneVerts[8][1]),
                   -sceneVerts[9][2]);
    
    Pnt3f lightMax(osgMin(camVerts[9][0], sceneVerts[9][0]),
                   osgMin(camVerts[9][1], sceneVerts[9][1]),
                   -sceneVerts[8][2]);

    // enlarge by 2% in x, y, z direction
    lightMin[0] -= (lightMax[0] - lightMin[0]) * 0.01f;
    lightMin[1] -= (lightMax[1] - lightMin[1]) * 0.01f; 
    lightMin[2] -= (lightMax[2] - lightMin[2]) * 0.01f;

    lightMax[0] += (lightMax[0] - lightMin[0]) * 0.01f;
    lightMax[1] += (lightMax[1] - lightMin[1]) * 0.01f;
    lightMax[2] += (lightMax[2] - lightMin[2]) * 0.01f;

    Matrix matLightProj;
    Matrix matLightProjTrans;

    MatrixOrthogonal(matLightProj,
                     lightMin[0], lightMax[0],
                     lightMin[1], lightMax[1],
                     lightMin[2], lightMax[2] );

    updateShadowTexImage  (data);
    updateShadowTexBuffers(data);
    updateRenderTargets   (data);

    Int32 shadowTexUnit = (this->getForceTextureUnit() > 0) ?
                           this->getForceTextureUnit()      : 7;

    ShaderProgram *shadowFP = this->getShadowFragmentProgram();

    if(shadowFP == NULL)
    {
        ShaderProgramUnrecPtr newShadowFP = ShaderProgram::createLocal();
        newShadowFP->setShaderType(GL_FRAGMENT_SHADER);
        newShadowFP->setProgram   (_dirFPCode        );

        newShadowFP->addUniformVariable("SSME_matEyeToLight", matEyeToLight);
        newShadowFP->addUniformVariable("SSME_matLightProj",  matLightProj );
        newShadowFP->addUniformVariable("SSME_texShadow",     shadowTexUnit);

        this->setShadowFragmentProgram(newShadowFP);
        shadowFP = newShadowFP;
    }
    else
    {
        shadowFP->updateUniformVariable("SSME_matEyeToLight", matEyeToLight);
        shadowFP->updateUniformVariable("SSME_matLightProj",  matLightProj );
    }

    commitChanges();

    this->pushPartition(ract);
    {
        RenderPartition   *part   = ract->getActivePartition( );
        Window            *win    = ract->getWindow         ( );
        FrameBufferObject *target = data->getRenderTargets  (0);
        Background        *back   = data->getBackground     ( );

        part->setRenderTarget(target);
        part->setWindow      (win   );

        part->calcViewportDimension(0.f, 0.f, 1.f, 1.f,
                                    target->getWidth (),
                                    target->getHeight() );

        part->setupProjection(matLightProj, matLightProjTrans);
        part->setupViewing   (matWorldToLight                );

        part->setNear        (parentPart->getNear());
        part->setFar         (parentPart->getFar ());

        part->calcFrustum    (                     );

        part->setBackground  (back                 );

        // force material for shadow map generation
        part->overrideMaterial(data->getLightPassMaterials(0),
                               ract->getActNode           ( ) );

        this->recurseFrom(ract, dirL);
        ract->useNodeList(false     );

        // undo override
        part->overrideMaterial(NULL,
                               ract->getActNode           ( ) );
    }
    this->popPartition(ract);
}
void TrapezoidalShadowMapEngine::handlePointLightEnter(
    PointLight *pointL, RenderAction *ract, TSMEngineData *data)
{
    RenderPartition *parentPart = ract->getActivePartition();
    
    Matrixr matEyeToWorld(parentPart->getCameraToWorld());  
    Matrixr matLightProj;

    Real    shadowNear = (getShadowNear() != 0.f ? 
                          getShadowNear()       : 
                          parentPart->getNear()  );
    Real    shadowFar  = (getShadowFar () != 0.f ?
                          getShadowFar ()       :
                          parentPart->getFar()   );

    Inherited::calcPointLightRange(
        pointL, 0.01f,
        shadowNear, shadowFar, shadowNear, shadowFar);

    MatrixPerspective(matLightProj, Pi / 4.f, 1.f,
                      shadowNear, shadowFar       );
    
    Matrixr   matWorldToLight;
    Matrixr   matEyeToLight;
    MFMatrixr mfMatNT;

    mfMatNT.resize(6);

    Inherited::calcPointLightMatrices(matWorldToLight, matEyeToLight,
                                      pointL,          matEyeToWorld );

    Inherited::updatePointLightShadowTexImage  (data);
    Inherited::updatePointLightShadowTexBuffers(data);
    Inherited::updatePointLightRenderTargets   (data);

    Int32          shadowTexUnit = (this->getForceTextureUnit() >= 0) ?
                                    this->getForceTextureUnit()       : 7;
    ShaderProgram *shadowFP      = this->getShadowFragmentProgram();

    if(shadowFP == NULL)
    {
        ShaderProgramUnrecPtr newShadowFP = ShaderProgram::createLocal();
        newShadowFP->setShaderType(GL_FRAGMENT_SHADER);
        newShadowFP->setProgram   (_pointFPCode      );
        
        newShadowFP->addUniformVariable("TSME_matEyeToLight", matEyeToLight);
        newShadowFP->addUniformVariable("TSME_matLightProj",  matLightProj );
        newShadowFP->addUniformVariable("TSME_matNT",         mfMatNT      );
        newShadowFP->addUniformVariable("TSME_texShadow",     shadowTexUnit);
        newShadowFP->addUniformVariable("TSME_texShadowSizeInv",
                                        Vec2f(1.f / getWidth (), 
                                              1.f / getHeight() )          );
        
        this->setShadowFragmentProgram(newShadowFP);
        shadowFP = newShadowFP;
    }
    else
    {
        shadowFP->updateUniformVariable("TSME_matEyeToLight", matEyeToLight);
        shadowFP->updateUniformVariable("TSME_matLightProj",  matLightProj );
    }

    const FrustumVolume &eyeFrust = parentPart->getFrustum();

    for(UInt16 faceIdx = 0; faceIdx < 6; ++faceIdx)
    {
        Matrixr matWorldToLightFace (matWorldToLight         );
        matWorldToLightFace.multLeft(_matCubeFaceInv[faceIdx]);

        Matrixr matLightFull(matWorldToLightFace);
        matLightFull.multLeft(matLightProj);

        FrustumVolume lightFrust;
        Matrixr       matNT;

        lightFrust.setPlanes(matLightFull);

        bool matNTValid = 
            calcTrapezoidalTransform(mfMatNT[faceIdx],
                                     matEyeToWorld,    matLightFull,
                                     eyeFrust,         lightFrust   );

        if(matNTValid == false)
        {
            // setup a minimal partition to clear the cube face

            commitChanges();

            this->pushPartition(ract,
                                RenderPartition::CopyNothing,
                                RenderPartition::SimpleCallback);
            {
                RenderPartition   *part   = ract->getActivePartition(       );
                Window            *win    = ract->getWindow         (       );
                FrameBufferObject *target = data->getRenderTargets  (faceIdx);
                Background        *back   = data->getBackground     (       );

                part->setSetupMode(RenderPartition::ViewportSetup  |
                                   RenderPartition::BackgroundSetup );

                part->setRenderTarget(target);
                part->setWindow      (win   );

                part->calcViewportDimension(0.f, 0.f, 1.f, 1.f,
                                            target->getWidth (),
                                            target->getHeight() );
                
                part->setBackground(back);

                RenderPartition::SimpleDrawCallback emptyCubeFaceDraw =
                    boost::bind(
                        &TrapezoidalShadowMapEngine::emptyCubeFaceDrawFunc,
                        this, _1);

                part->dropFunctor(emptyCubeFaceDraw);
            }
            this->popPartition(ract);
        }
        else
        {
            updateLightPassMaterial(data, faceIdx, mfMatNT[faceIdx]);
        
            commitChanges();

            this->pushPartition(ract);
            {
                RenderPartition   *part   = ract->getActivePartition(       );
                Window            *win    = ract->getWindow         (       );
                FrameBufferObject *target = data->getRenderTargets  (faceIdx);
                Background        *back   = data->getBackground     (       );

                part->setRenderTarget(target);
                part->setWindow      (win   );

                part->calcViewportDimension(0.f, 0.f, 1.f, 1.f,
                                            target->getWidth (),
                                            target->getHeight() );

                part->setupProjection(matLightProj, Matrixr::identity());
                part->setupViewing   (matWorldToLightFace              );

                part->setNear        (parentPart->getNear());
                part->setFar         (parentPart->getFar ());
            
                part->setFrustum     (lightFrust           );
                
                part->setBackground  (back                 );

                part->overrideMaterial(data->getLightPassMaterials(faceIdx),
                                       ract->getActNode           (       ) );

                this->recurseFrom(ract, pointL);
                ract->useNodeList(false       );

                part->overrideMaterial(NULL,
                                       ract->getActNode           (       ) );
            }
            this->popPartition(ract);
        }
    }

    shadowFP->updateUniformVariable("TSME_matNT", mfMatNT);
}