MaterialPtr AmbientOcclusionScene::createAmbientOcclusionMaterial( const QString& diffuse,
                                                                   const QString& aoFactors ) const
{
    MaterialPtr material( new Material );

    // Setup the shaders
    material->setShaders( ":/shaders/ambientocclusion.vert",
                          ":/shaders/ambientocclusion.frag" );

    // Create a diffuse texture
    TexturePtr diffuseTexture( new Texture( Texture::Texture2D ) );
    diffuseTexture->create();
    diffuseTexture->bind();
    diffuseTexture->setImage( QImage( diffuse ) );

    // Create the ambient occlusion factors texture
    TexturePtr aoTexture( new Texture( Texture::Texture2D ) );
    aoTexture->create();
    aoTexture->bind();
    aoTexture->setImage( QImage( aoFactors ) );

#if !defined(Q_OS_MAC)
    // Create a sampler. This can be shared by both textures
    SamplerPtr sampler( new Sampler );
    sampler->create();
    sampler->setWrapMode( Sampler::DirectionS, GL_CLAMP_TO_EDGE );
    sampler->setWrapMode( Sampler::DirectionT, GL_CLAMP_TO_EDGE );
    sampler->setMinificationFilter( GL_LINEAR );
    sampler->setMagnificationFilter( GL_LINEAR );

    // We associate the diffuse texture with texture unit 0 and
    // the ao factors texture with unit 1
    material->setTextureUnitConfiguration( 0, diffuseTexture, sampler, "diffuseTexture" );
    material->setTextureUnitConfiguration( 1, aoTexture, sampler, "ambientOcclusionTexture" );
#else
    diffuseTexture->bind();
    diffuseTexture->setWrapMode( Texture::DirectionS, GL_CLAMP_TO_EDGE );
    diffuseTexture->setWrapMode( Texture::DirectionT, GL_CLAMP_TO_EDGE );
    diffuseTexture->setMinificationFilter( GL_LINEAR );
    diffuseTexture->setMagnificationFilter( GL_LINEAR );

    aoTexture->bind();
    aoTexture->setWrapMode( Texture::DirectionS, GL_CLAMP_TO_EDGE );
    aoTexture->setWrapMode( Texture::DirectionT, GL_CLAMP_TO_EDGE );
    aoTexture->setMinificationFilter( GL_LINEAR );
    aoTexture->setMagnificationFilter( GL_LINEAR );

    // We associate the diffuse texture with texture unit 0 and
    // the ao factors texture with unit 1
    material->setTextureUnitConfiguration( 0, diffuseTexture, "diffuseTexture" );
    material->setTextureUnitConfiguration( 1, aoTexture, "ambientOcclusionTexture" );
#endif
    return material;
}
bool LightingShader::asFragmentProcessor(GrContext* context, const SkPaint& paint, 
                                         const SkMatrix& viewM, const SkMatrix* localMatrix, 
                                         GrColor* color, GrProcessorDataManager*,
                                         GrFragmentProcessor** fp) const {
    // we assume diffuse and normal maps have same width and height
    // TODO: support different sizes
    SkASSERT(fDiffuseMap.width() == fNormalMap.width() &&
             fDiffuseMap.height() == fNormalMap.height());
    SkMatrix matrix;
    matrix.setIDiv(fDiffuseMap.width(), fDiffuseMap.height());

    SkMatrix lmInverse;
    if (!this->getLocalMatrix().invert(&lmInverse)) {
        return false;
    }
    if (localMatrix) {
        SkMatrix inv;
        if (!localMatrix->invert(&inv)) {
            return false;
        }
        lmInverse.postConcat(inv);
    }
    matrix.preConcat(lmInverse);

    // Must set wrap and filter on the sampler before requesting a texture. In two places below
    // we check the matrix scale factors to determine how to interpret the filter quality setting.
    // This completely ignores the complexity of the drawVertices case where explicit local coords
    // are provided by the caller.
    GrTextureParams::FilterMode textureFilterMode = GrTextureParams::kBilerp_FilterMode;
    switch (paint.getFilterQuality()) {
    case kNone_SkFilterQuality:
        textureFilterMode = GrTextureParams::kNone_FilterMode;
        break;
    case kLow_SkFilterQuality:
        textureFilterMode = GrTextureParams::kBilerp_FilterMode;
        break;
    case kMedium_SkFilterQuality:{                          
        SkMatrix matrix;
        matrix.setConcat(viewM, this->getLocalMatrix());
        if (matrix.getMinScale() < SK_Scalar1) {
            textureFilterMode = GrTextureParams::kMipMap_FilterMode;
        } else {
            // Don't trigger MIP level generation unnecessarily.
            textureFilterMode = GrTextureParams::kBilerp_FilterMode;
        }
        break;
    }
    case kHigh_SkFilterQuality:
    default:
        SkErrorInternals::SetError(kInvalidPaint_SkError,
            "Sorry, I don't understand the filtering "
            "mode you asked for.  Falling back to "
            "MIPMaps.");
        textureFilterMode = GrTextureParams::kMipMap_FilterMode;
        break;

    }

    // TODO: support other tile modes
    GrTextureParams params(kClamp_TileMode, textureFilterMode);
    SkAutoTUnref<GrTexture> diffuseTexture(GrRefCachedBitmapTexture(context, fDiffuseMap, &params));
    if (!diffuseTexture) {
        SkErrorInternals::SetError(kInternalError_SkError,
            "Couldn't convert bitmap to texture.");
        return false;
    }

    SkAutoTUnref<GrTexture> normalTexture(GrRefCachedBitmapTexture(context, fNormalMap, &params));
    if (!normalTexture) {
        SkErrorInternals::SetError(kInternalError_SkError,
            "Couldn't convert bitmap to texture.");
        return false;
    }

    GrColor lightColor = GrColorPackRGBA(SkColorGetR(fLight.fColor), SkColorGetG(fLight.fColor),
                                         SkColorGetB(fLight.fColor), SkColorGetA(fLight.fColor));
    GrColor ambientColor = GrColorPackRGBA(SkColorGetR(fAmbientColor), SkColorGetG(fAmbientColor),
                                           SkColorGetB(fAmbientColor), SkColorGetA(fAmbientColor));

    *fp = SkNEW_ARGS(LightingFP, (diffuseTexture, normalTexture, matrix,
                                  fLight.fDirection, lightColor, ambientColor));
    *color = GrColorPackA4(paint.getAlpha());
    return true;
}