std::vector<PathNode> BidirectionalIntegrator::generateEyePath(Ray r)
{
    std::vector<PathNode> eyePath;
    eyePath.clear();

    Intersection isx = intersection_engine->GetIntersection(r);
    int depth=0;
    while(depth<max_depth && isx.t > 0)
    {
        // store the path node
        PathNode node;
        node.isx = isx;
        node.dirIn_world = -r.direction;
        node.dirIn_local = isx.ToLocalNormalCoordinate(-r.direction);
        node.F = isx.object_hit->material->SampleAndEvaluateScatteredEnergy(isx,node.dirIn_local,node.dirOut_local,node.pdf);
        node.dirOut_world = isx.ToWorldNormalCoordinate(node.dirOut_local);

        if(node.pdf != 0)
            eyePath.push_back(node);
        else
            break;

        //update r
        r = Ray(isx.point + glm::sign(glm::dot(node.dirOut_world,isx.normal)) * isx.normal*1e-3f, node.dirOut_world);

        // update isx and depth info
        depth++;
        isx = intersection_engine->GetIntersection(r);

    }

    return eyePath;
}
// MIS: sampling BRDF
glm::vec3 BidirectionalIntegrator::MIS_SampleBRDF(Intersection &intersection, Ray &r, Geometry* &light)
{
    if(Number_BRDF == 0)
        return glm::vec3(0);

    // Direct light estimator: sample BRDF
    glm::vec3 sum_brdf_sample(0.0f);
    for(int i = 0; i < Number_BRDF; i++)
    {
        glm::vec3 wo_local = intersection.ToLocalNormalCoordinate(-r.direction);
        glm::vec3 wj_local;
        float pdf_brdf;
        glm::vec3 F = intersection.object_hit->material->SampleAndEvaluateScatteredEnergy(intersection,wo_local,wj_local,pdf_brdf);

        glm::vec3 wj_world = intersection.ToWorldNormalCoordinate(wj_local);
        glm::vec3 wo_world = - r.direction;

        Intersection isxOnLight = intersection_engine->GetIntersection(Ray(intersection.point+float(1e-3)*intersection.normal, wj_world));

        if(isxOnLight.t > 0 && isxOnLight.object_hit == light && pdf_brdf > 0)
        {
            float temp,pdf_light = light->RayPDF(intersection, Ray(intersection.point, wj_world));
            float W = PowerHeuristic(pdf_brdf,float(Number_BRDF),pdf_light,float(Number_Light));
            glm::vec3 Ld = light->material->EvaluateScatteredEnergy(isxOnLight,wo_world,-wj_world,temp);

            if(pdf_light > 0 )
            {
                if(isinf(pdf_brdf)) // delta specular surface
                {
                    sum_brdf_sample = sum_brdf_sample +
                            F * Ld * float(fabs(glm::dot(wj_world, intersection.normal))) / pdf_light;
                }
                else
                {
                    sum_brdf_sample = sum_brdf_sample +
                            W * F * Ld * float(fabs(glm::dot(wj_world,intersection.normal))) / pdf_brdf;
                }
            }
        }

    }
    return sum_brdf_sample / float(Number_BRDF);
}
std::vector<PathNode> BidirectionalIntegrator::generateLightPath(Geometry* &light)
{
    std::vector<PathNode> lightPath;
    lightPath.clear();

    Intersection lightSample = light->RandomSampleOnSurface(uniform_distribution(generator),uniform_distribution(generator));

    Ray r(lightSample.point, lightSample.point - light->transform.position());

    Intersection isx = intersection_engine->GetIntersection(r);
    int depth = 0;
    while(isx.t > 0 && depth < max_depth)
    {
        // store pathnode on the light path
        PathNode node;
        node.isx = isx;
        node.dirIn_world = -r.direction;
        node.dirIn_local = isx.ToLocalNormalCoordinate(-r.direction);
        node.F = isx.object_hit->material->SampleAndEvaluateScatteredEnergy(isx,node.dirIn_local,node.dirOut_local,node.pdf);
        node.dirOut_world = isx.ToWorldNormalCoordinate(node.dirOut_local);

        if(node.pdf != 0)
            lightPath.push_back(node);
        else
            break;

        //update r
        r = Ray(isx.point + glm::sign(glm::dot(node.dirOut_world,isx.normal)) * isx.normal*1e-3f, node.dirOut_world);

        // update isx and depth info
        depth++;
        isx = intersection_engine->GetIntersection(r);
    }

    return lightPath;
}