Spectrum PathTracer::Integration (
	const Scene * thisScene,
	const Ray & ray,
	size_t currentDepth
) const
{
	// Russian Roulette
	// probility to stop this path
	double rr = 0.01;
	double randnum = getUniformRandomNumber( 0.0, 0.99 );
	if ( randnum < rr && currentDepth > 5 )
		return Spectrum();

	if ( currentDepth == maxDepth )
		return Spectrum();

	Intersection intersectionInfo;

	// check intersection between ray and scene
	if ( ! thisScene->IntersectionWithScene( ray, intersectionInfo ) )
		return Spectrum();

	// if there is an intersection
	Spectrum intensity;
	Spectrum emission;

	Objects * obj = intersectionInfo.object;

	if ( obj->emitter )
	{
		emission = obj->emission;
	}

	BRDFModels * brdf = obj->brdf_model;

	// Get surface (macro) normal direction
	Vector geometryNormalDirection ( 0 , 0 , 0 );
	Vector sampledNextDirection ( 0 , 0 , 0 );

	geometryNormalDirection = intersectionInfo.normal;

	double pdf = 0;

	Attenuation attenuationFactor = 
		brdf->SampleBRDF(
			ray.direction,
			sampledNextDirection,
			geometryNormalDirection,
			pdf
		);

	Ray newRay(
		intersectionInfo.intersectionPoint,
		sampledNextDirection
	);

	Spectrum Li =
		Integration(
			thisScene,
			newRay,
			currentDepth + 1
		);


	if ( !Li.isEmpty() )
	{
		attenuationFactor.Scaled_by_Spectrum_Value( obj->material_type->color );

		double cos_i = AbsDot( sampledNextDirection, geometryNormalDirection );

		intensity +=
			Attenuate_Light_Spectrum( attenuationFactor, Li ) * cos_i / pdf;
	}

	// weight the radiance back after Ruassian Roulette
	intensity = ( currentDepth > 5 ) ? ( intensity / ( 1 - rr ) ) : intensity;

	ASSERT_NAN( intensity );

	return emission + intensity;
}
// with direct illumination
// this one actually does not do the right things on DI with MIS
// left only for reference
Spectrum PathTracer::IntegrationWithDI (
	const Scene * thisScene,
	const Ray & ray,
	size_t currentDepth
) const
{
	// Russian Roulette
	// probility to stop this path
	double rr = 0.01;
	double randnum = getUniformRandomNumber( 0.0, 1.0 );
	if ( randnum < rr && currentDepth > 5 )
		return Spectrum();

	if ( currentDepth == maxDepth )
		return Spectrum();

	Intersection intersectionInfo;

	// check intersection between ray and scene
	if ( !thisScene->IntersectionWithScene( ray, intersectionInfo ) )
		return Spectrum();

	// if there is an intersection
	Spectrum intensity;
	Spectrum lightSourceSamplingContribution;
	Spectrum brdfSamplingContribution;

	double brdf_pdf = 0;
	double light_pdf = 0;

	double brdf_weight = 0;
	double light_weight = 0;

	Objects * obj = intersectionInfo.object;

	if ( obj->emitter )
	{
		intensity += obj->emission;
	}


	BRDFModels * brdf = obj->brdf_model;


	// **************** evaluate direction illumination ******
	if ( !obj->emitter )
		EstimateDirectIllumination(
			thisScene,
			& intersectionInfo,
			ray,
			light_pdf,
			lightSourceSamplingContribution
		);

	// ******************** end of direct illumination **********************

	// BRDF sampling is evaluated twice????

	Vector geometryNormalDirection;

	geometryNormalDirection = intersectionInfo.normal;

	Vector sampledNextDirection( 0, 0, 0 );
	Attenuation attenuationFactor;

	attenuationFactor = brdf->SampleBRDF(
		ray.direction,
		sampledNextDirection,
		geometryNormalDirection,
		brdf_pdf
	);

	Ray newRay(
		intersectionInfo.intersectionPoint,
		sampledNextDirection
	);

	Spectrum Li =
		IntegrationWithDI(
			thisScene,
			newRay,
			currentDepth + 1
		);

	if ( !Li.isEmpty() )
	{
		attenuationFactor.Scaled_by_Spectrum_Value( obj->material_type->color );

		double cos_i = AbsDot( sampledNextDirection, geometryNormalDirection );

		brdfSamplingContribution += Attenuate_Light_Spectrum( attenuationFactor, Li ) * cos_i;
	}


	if ( light_pdf == 0 && brdf_pdf == 0 )
	{
		return intensity;
	}
	// MIS weight
	BalanceHeuristic( 1, light_pdf, 1, brdf_pdf, light_weight, brdf_weight );

	intensity += lightSourceSamplingContribution * light_weight;
	intensity += brdfSamplingContribution * brdf_weight;

	intensity = ( currentDepth > 5 ) ? ( intensity / ( 1 - rr ) ) : intensity;


	ASSERT_NAN( intensity );

	return intensity;
}