void ShaderShadowMapEngine::handleSpotLightEnter(
    SpotLight *spotL, RenderAction *ract, SSMEngineData *data)
{
    RenderPartition *parentPart    = ract->getActivePartition();
//    Real             cosSpotCutOff = osgCos(spotL->getSpotCutOff());

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

    calcSpotLightMatrices(matWorldToLight, matEyeToLight, 
                          spotL,           matEyeToWorld );

    Real32  lightNear;
    Real32  lightFar;

    calcPointLightRange(spotL, 0.001f,
                        parentPart->getNear(), parentPart->getFar(),
                        lightNear,             lightFar             );

    if(getShadowNear() != 0.f)
    {
        lightNear = getShadowNear();
    }

    if(getShadowFar() != 0.f)
    {
        lightFar  = getShadowFar();
    }

    Matrix matLightProj;
    Matrix matLightProjTrans;

    MatrixPerspective(matLightProj, 
                      spotL->getSpotCutOff(), 1.f,
                      lightNear, lightFar         );
    
    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   (_spotFPCode       );

        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, spotL);
        ract->useNodeList(false      );

        // undo override
        part->overrideMaterial(NULL,
                               ract->getActNode           ( ) );
    }
    this->popPartition(ract);
}
void ShaderShadowMapEngine::handlePointLightEnter(
    PointLight *pointL, RenderAction *ract, SSMEngineData *data)
{
    RenderPartition *parentPart = ract->getActivePartition();

    Matrix matEyeToWorld(parentPart->getCameraToWorld());
    Matrix matLightProj;

    Real32 lightNear;
    Real32 lightFar;

    calcPointLightRange(pointL, 0.001f,
                        parentPart->getNear(), parentPart->getFar(),
                        lightNear,             lightFar             );

    MatrixPerspective(matLightProj, Pi / 4.f, 1.f,
                      lightNear, lightFar         );

    Matrix matWorldToLight;
    Matrix matEyeToLight;

    calcPointLightMatrices(matWorldToLight, matEyeToLight,
                           pointL,          matEyeToWorld );

    updatePointLightShadowTexImage  (data);
    updatePointLightShadowTexBuffers(data);
    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("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();

    // schedule rendering of cube faces
    for(UInt16 i = 0; i < 6; ++i)
    {
        Matrix matWorldToLightFace(matWorldToLight);
        matWorldToLightFace.multLeft(_matCubeFaceInv[i]);

        this->pushPartition(ract);
        {
            RenderPartition   *part   = ract->getActivePartition( );
            Window            *win    = ract->getWindow         ( );
            FrameBufferObject *target = data->getRenderTargets  (i);
            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, Matrix::identity());
            part->setupViewing   (matWorldToLightFace             );

            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, pointL);
            ract->useNodeList(false       );

            // undo override
            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 ComputeShaderChunk::changed(ConstFieldMaskArg whichField, 
                             UInt32            origin,
                             BitVector         details)
{
    bool bMarkChanged = false;

    if(0x0000 != (whichField & ComputeProgramFieldMask) &&
       0      != _sfComputeProgram.getValue().size()     )
    {
        if(_mfComputeShader.size() == 0)
        {
            ShaderProgramUnrecPtr pProg = ShaderProgram::createDependent(
                this->getFieldFlags()->_bNamespaceMask);

            pProg->setShaderType(GL_COMPUTE_SHADER);

            addComputeShader(pProg);

            pProg->setVariables(this->getVariables());
        }
        else if(_mfComputeShader.size() > 1)
        {
            editMFComputeShader()->resize(1);
        }
        
        _mfComputeShader[0]->setProgram(_sfComputeProgram.getValue());

        bMarkChanged = true;

        Window::reinitializeGLObject(this->getGLId());
    }


    if(0x0000 != (whichField & VariablesFieldMask))
    {
        ShaderProgramVariables *pVars = _sfVariables.getValue();

        if(pVars != NULL)
        {
            if(details == 0x0001)
            {
                // be save reset all locations

                if(pVars->getMFVariables()->size() == 0)
                {
                    editMFVariableLocations()->clear();
                }
                else
                {
                    editMFVariableLocations()->resize(
                        pVars->getMFVariables()->size(), 
                        -1);

                    std::fill(editMFVariableLocations()->begin(),
                              editMFVariableLocations()->end  (),
                              -1);
                }

                // be save reset all locations

                if(pVars->getMFProceduralVariables()->size() == 0     )
                {
                    editMFProceduralVariableLocations()->clear();
                }
                else
                {
                    editMFProceduralVariableLocations()->resize(
                        pVars->getMFProceduralVariables()->size(), 
                        -1);

                    std::fill(editMFProceduralVariableLocations()->begin(),
                              editMFProceduralVariableLocations()->end  (),
                              -1);
                }
            }
        }

        Window::refreshGLObject(this->getGLId());
    }

    if(0x0000 != (whichField & ComputeShaderFieldMask))
    {
        bMarkChanged = true;

        Window::reinitializeGLObject(this->getGLId());
    }

    if(bMarkChanged == true)
    {
        // be save reset all locations

        std::fill(editMFVariableLocations()->begin(),
                  editMFVariableLocations()->end  (),
                  -1);

        std::fill(editMFProceduralVariableLocations()->begin(),
                  editMFProceduralVariableLocations()->end  (),
                  -1);
    
        if(_sfVariables.getValue() != NULL)
        {
            _sfVariables.getValue()->markAllChanged();
        }
    }

    Inherited::changed(whichField, origin, details);
}
void TrapezoidalShadowMapEngine::updateLightPassMaterial(
    TSMEngineData *data, UInt16 faceIdx, const Matrixr &matNT)
{
    if(data->getMFLightPassMaterials()->size() < 6)
        data->editMFLightPassMaterials()->resize(6, NULL);

    ChunkMaterial *lightPassMat = data->getLightPassMaterials(faceIdx);

    if(lightPassMat == NULL)
    {
        ColorMaskChunkUnrecPtr newCMaskChunk = ColorMaskChunk::create();
        newCMaskChunk->setMaskR(false);
        newCMaskChunk->setMaskG(false);
        newCMaskChunk->setMaskB(false);
        newCMaskChunk->setMaskA(false);

#ifdef OSG_TSME_LIGHTPASS_EXACT

        ShaderProgramUnrecPtr newLightPassVP = ShaderProgram::createLocal();
        newLightPassVP->setShaderType(GL_VERTEX_SHADER);
        newLightPassVP->setProgram   (_lightPassVPCode);
        newLightPassVP->addUniformVariable("TSME_matNT", matNT);

        ShaderProgramUnrecPtr newLightPassFP = ShaderProgram::createLocal();
        newLightPassFP->setShaderType(GL_FRAGMENT_SHADER);
        newLightPassFP->setProgram   (_lightPassFPCode  );
        newLightPassFP->addUniformVariable("TSME_offsetFactor", getOffsetFactor());
        newLightPassFP->addUniformVariable("TSME_offsetBias",   getOffsetBias  ());

        ShaderProgramChunkUnrecPtr newLightPassSHChunk =
            ShaderProgramChunk::createLocal();
        newLightPassSHChunk->addVertexShader  (newLightPassVP);
        newLightPassSHChunk->addFragmentShader(newLightPassFP);

        ChunkMaterialUnrecPtr  newLightPassMat = ChunkMaterial::createLocal();
        newLightPassMat->addChunk(newCMaskChunk      );
        newLightPassMat->addChunk(newLightPassSHChunk);

        lightPassMat = newLightPassMat;
        data->editMFLightPassMaterials()->replace(faceIdx, newLightPassMat);

#else
        
        PolygonChunkUnrecPtr newPolyChunk = PolygonChunk::createLocal();
        newPolyChunk->setOffsetFill  (true             );
        newPolyChunk->setOffsetFactor(getOffsetFactor());
        newPolyChunk->setOffsetBias  (getOffsetBias  ());

        ShaderProgramUnrecPtr newLightPassVP = ShaderProgram::createLocal();
        newLightPassVP->setShaderType     (GL_VERTEX_SHADER   );
        newLightPassVP->setProgram        (_lightPassVPCode   );
        newLightPassVP->addUniformVariable("TSME_matNT", matNT);

        ShaderProgramChunkUnrecPtr newLightPassSHChunk =
            ShaderProgramChunk::createLocal();
        newLightPassSHChunk->addVertexShader(newLightPassVP);

        ChunkMaterialUnrecPtr  newLightPassMat = ChunkMaterial::createLocal();
        newLightPassMat->addChunk(newCMaskChunk      );
        newLightPassMat->addChunk(newPolyChunk       );
        newLightPassMat->addChunk(newLightPassSHChunk);

        lightPassMat = newLightPassMat;
        (*data->editMFLightPassMaterials())[faceIdx] = newLightPassMat;

#endif

        
    }
    else
    {
        ShaderProgramChunk *lightPassSHChunk =
            dynamic_cast<ShaderProgramChunk *>(
                lightPassMat->find(ShaderProgramChunk::getClassType()));
    
        if(lightPassSHChunk != NULL)
        {
            ShaderProgram *lightPassVP = lightPassSHChunk->getVertexShader(0);
            lightPassVP->updateUniformVariable("TSME_matNT", matNT);
        }
    }
}
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 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);
}
Action::ResultE
GPUSkinningAlgorithm::renderEnter(Action *action)
{
    Action::ResultE  res     = Action::Continue;
    SkinnedGeometry *skinGeo = getSkin    ();
    Skeleton        *skel    = getSkeleton();
    RenderAction    *ract    =
        boost::polymorphic_downcast<RenderAction *>(action); 

    OSG_ASSERT(skinGeo != NULL);
    OSG_ASSERT(skel    != NULL);

    GPUSkinningDataAttachmentUnrecPtr data = getGPUSkinningData(skel);

    if(data == NULL)
    {
        data = GPUSkinningDataAttachment::create();
        skel->addAttachment(data);
    }

    skel->renderEnter(action, skinGeo);

    ShaderProgramChunkUnrecPtr shCode = data->getShaderCode();

    if(shCode == NULL)
    {
        shCode = ShaderProgramChunk::create();
        data->setShaderCode(shCode);

        ShaderProgramUnrecPtr vp = ShaderProgram::createVertexShader();
        vp->setProgram(_vpVertexSkinning);

        shCode->addShader(vp);

        vp->addUniformVariable(
            "matJoints",    (*skel->getMFJointMatrices()));
        vp->addUniformVariable(
            "matBindShape", skinGeo->getBindShapeMatrix());
    }
    else if(data->getDataValid() == false)
    {
        ShaderProgram *vp = shCode->getVertexShader(0);
        OSG_ASSERT(vp != NULL);

        vp->updateUniformVariable(
            "matJoints",    (*skel->getMFJointMatrices()));
        vp->updateUniformVariable(
            "matBindShape", skinGeo->getBindShapeMatrix());

        data->setDataValid(true);
    }

    ract->pushState();
    {
        ract->addOverride(ShaderProgramChunk::getStaticClassId(),
                          shCode                                 );

        res = skinGeo->SkinnedGeometry::Inherited::renderEnter(ract);
    }
    ract->popState ();

    return res;
}