void HitPoints::TraceEyePath(HitPoint *hp, const Sample &sample, float const invPixelPdf)
{
	HitPointEyePass *hpep = &hp->eyePass;

	Scene &scene(*renderer->scene);
	const bool includeEnvironment = renderer->sppmi->includeEnvironment;
	const u_int maxDepth = renderer->sppmi->maxEyePathDepth;

	//--------------------------------------------------------------------------
	// Following code is, given or taken, a copy of path integrator Li() method
	//--------------------------------------------------------------------------

	// Declare common path integration variables
	const SpectrumWavelengths &sw(sample.swl);
	Ray ray;
	const float rayWeight = sample.camera->GenerateRay(scene, sample, &ray, &(hp->imageX), &(hp->imageY));

	const float nLights = scene.lights.size();
	const u_int lightGroupCount = scene.lightGroups.size();

	vector<SWCSpectrum> Ld(lightGroupCount, 0.f);
	// Direct lighting samples variance
	vector<float> Vd(lightGroupCount, 0.f);
	SWCSpectrum pathThroughput(1.0f); // pathThroughput is normalised perpixel for the eyepass contribution, so no need for invPixelPdf normalisation;
	vector<SWCSpectrum> L(lightGroupCount, 0.f);
	vector<float> V(lightGroupCount, 0.f);
	float VContrib = .1f;

	bool scattered = false;
	hpep->alpha = 1.f;
	hpep->distance = INFINITY;
	u_int vertexIndex = 0;
	const Volume *volume = NULL;

	bool specularBounce = true;
	const bool enableDirectLightSampling = renderer->sppmi->directLightSampling;

	for (u_int pathLength = 0; ; ++pathLength) {
		const SWCSpectrum prevThroughput(pathThroughput);

		float *data = eyeSampler->GetLazyValues(sample, 0, pathLength);

		// Find next vertex of path
		Intersection isect;
		BSDF *bsdf;
		float spdf;
		sample.arena.Begin();
		if (!scene.Intersect(sample, volume, scattered, ray, data[0],
			&isect, &bsdf, &spdf, NULL, &pathThroughput)) {
			pathThroughput /= spdf;
			// Dade - now I know ray.maxt and I can call volumeIntegrator
			SWCSpectrum Lv;
			u_int g = scene.volumeIntegrator->Li(scene, ray, sample,
				&Lv, &hpep->alpha);
			if (!Lv.Black()) {
				Lv *= prevThroughput;
				L[g] += Lv;
			}

			// Stop path sampling since no intersection was found
			// Possibly add horizon in render & reflections
			if (!enableDirectLightSampling || (
					(includeEnvironment || vertexIndex > 0) && specularBounce)) {
				BSDF *ibsdf;
				for (u_int i = 0; i < nLights; ++i) {
					SWCSpectrum Le(pathThroughput);
					if (scene.lights[i]->Le(scene, sample,
						ray, &ibsdf, NULL, NULL, &Le))
						L[scene.lights[i]->group] += Le;
				}
			}

			// Set alpha channel
			if (vertexIndex == 0)
				hpep->alpha = 0.f;

			hp->SetConstant();
			break;
		}
		sample.arena.End();
		scattered = bsdf->dgShading.scattered;
		pathThroughput /= spdf;
		if (vertexIndex == 0)
			hpep->distance = ray.maxt * ray.d.Length();

		SWCSpectrum Lv;
		const u_int g = scene.volumeIntegrator->Li(scene, ray, sample,
			&Lv, &hpep->alpha);
		if (!Lv.Black()) {
			Lv *= prevThroughput;
			L[g] += Lv;
		}

		// Possibly add emitted light at path vertex
		Vector wo(-ray.d);
		if (specularBounce) {
			BSDF *ibsdf;
			SWCSpectrum Le(pathThroughput);
			if (isect.Le(sample, ray, &ibsdf, NULL, NULL, &Le)) {
				L[isect.arealight->group] += Le;
				V[isect.arealight->group] += Le.Filter(sw) * VContrib;
			}
		}


		const Point &p = bsdf->dgShading.p;
		const Normal &n = bsdf->dgShading.nn;

		// Estimate direct lighting
		if (renderer->sppmi->directLightSampling && (nLights > 0)) {
			for (u_int i = 0; i < lightGroupCount; ++i) {
				Ld[i] = 0.f;
				Vd[i] = 0.f;
			}

			renderer->sppmi->hints.SampleLights(scene, sample, p, n,
				wo, bsdf, pathLength, pathThroughput, Ld, &Vd);

			for (u_int i = 0; i < lightGroupCount; ++i) {
				L[i] += Ld[i];
				V[i] += Vd[i] * VContrib;
			}
		}

		// Choose between storing or bouncing the hitpoint on the surface
		bool const has_store_component = bsdf->NumComponents(store_component) > 0;
		bool const has_bounce_component = bsdf->NumComponents(bounce_component) > 0;

		float pdf_event;
		bool store;

		if (has_store_component && has_bounce_component)
		{
			// There is both bounce and store component, we choose with a
			// random number
			// TODO: do this by importance
			store = data[4] < .5f;
			pdf_event = 0.5;
		}
		else
		{
			// If there is only bounce/store component, we bounce/store
			// accordingly
			store = has_store_component;
			pdf_event = 1.f;
		}

		if(store)
		{
			hp->SetSurface();
			hpep->pathThroughput = pathThroughput * rayWeight / pdf_event * invPixelPdf;
			hpep->wo = wo;

			hpep->bsdf = bsdf;
			hpep->single = sw.single;
			sample.arena.Commit();
			break;
		}

		// Sample BSDF to get new path direction
		Vector wi;
		float pdf;
		BxDFType flags;
		SWCSpectrum f;
		if (pathLength == maxDepth || !bsdf->SampleF(sw, wo, &wi,
			data[1], data[2], data[3], &f, &pdf, bounce_component, &flags,
			NULL, true)) {
			hp->SetConstant();
			break;
		}

		if (flags != (BSDF_TRANSMISSION | BSDF_SPECULAR) ||
			!(bsdf->Pdf(sw, wi, wo, BxDFType(BSDF_TRANSMISSION | BSDF_SPECULAR)) > 0.f))
		{
			++vertexIndex;
			specularBounce = (flags & BSDF_SPECULAR) != 0;
		}

		pathThroughput *= f / pdf_event;
		if (pathThroughput.Black()) {
			hp->SetConstant();
			break;
		}

		ray = Ray(p, wi);
		ray.time = sample.realTime;
		volume = bsdf->GetVolume(wi);
	}
	for(unsigned int i = 0; i < lightGroupCount; ++i)
	{
		if (!L[i].Black())
			V[i] /= L[i].Filter(sw);
		sample.AddContribution(hp->imageX, hp->imageY,
			XYZColor(sw, L[i]) * rayWeight, hp->eyePass.alpha, hp->eyePass.distance,
			0, renderer->sppmi->bufferEyeId, i);
	}
}
void DistributedPath::LiInternal(const Scene &scene, const Sample &sample,
		const Volume *volume, bool scattered, const Ray &ray,
		vector<SWCSpectrum> &L, float *alpha, float *zdepth,
		u_int rayDepth, bool includeEmit, u_int &nrContribs) const
{
	Intersection isect;
	BSDF *bsdf;
	const float time = ray.time; // save time for motion blur
	const SpectrumWavelengths &sw(sample.swl);
	SWCSpectrum Lt(1.f);
	float spdf;

	if (scene.Intersect(sample, volume, scattered, ray,
		sample.sampler->GetOneD(sample, scatterOffset, rayDepth),
		&isect, &bsdf, &spdf, NULL, &Lt)) {
		// Evaluate BSDF at hit point
		Vector wo = -ray.d;
		const Point &p = bsdf->dgShading.p;
		const Normal &n = bsdf->dgShading.nn;

		if (rayDepth == 0) {
			// Set Zbuf depth
			const Vector zv(p - ray.o);
			*zdepth = zv.Length();

			// Override alpha
			if(bsdf->compParams->oA)
				*alpha = bsdf->compParams->A;

			// Compute emitted light if ray hit an area light source with Visibility check
			if(bsdf->compParams->tVl && includeEmit) {
				BSDF *ibsdf;
				SWCSpectrum Le(1.f);
				if (isect.Le(sample, ray, &ibsdf, NULL, NULL,
					&Le)) {
					L[isect.arealight->group] += Le;
					++nrContribs;
				}
			}

			// Visibility check
			if(!bsdf->compParams->tVm) {
			       if (!bsdf->compParams->oA)
					*alpha = 0.f;
				return;
			}
		} else {

			// Compute emitted light if ray hit an area light source with Visibility check
			if(bsdf->compParams->tiVl && includeEmit) {
				BSDF *ibsdf;
				SWCSpectrum Le(1.f);
				if (isect.Le(sample, ray, &ibsdf, NULL, NULL,
					&Le)) {
					L[isect.arealight->group] += Le;
					++nrContribs;
				}
			}

			// Visibility check
			if(!bsdf->compParams->tiVm)
				return;
		}

		// Compute direct lighting for _DistributedPath_ integrator
		if (scene.lights.size() > 0) {
			const u_int samples = rayDepth > 0 ? indirectSamples :
				directSamples;
			const float invsamples = 1.f / samples;
			float lightSample[2], lightNum;
			float bsdfSample[2], bsdfComponent;
			for (u_int i = 0; i < samples; ++i) {
				// get samples
				if (rayDepth > 0) {
					const u_int index = i * rayDepth;
					sample.sampler->GetTwoD(sample,
						indirectLightSampleOffset,
						index, lightSample);
					lightNum = sample.sampler->GetOneD(sample, indirectLightNumOffset, index);
					sample.sampler->GetTwoD(sample,
						indirectBsdfSampleOffset,
						index, bsdfSample);
					bsdfComponent = sample.sampler->GetOneD(sample, indirectBsdfComponentOffset, index);
				} else {
					sample.sampler->GetTwoD(sample,
						lightSampleOffset, i,
						lightSample);
					lightNum = sample.sampler->GetOneD(sample, lightNumOffset, i);
					sample.sampler->GetTwoD(sample,
						bsdfSampleOffset, i,
						bsdfSample);
					bsdfComponent = sample.sampler->GetOneD(sample, bsdfComponentOffset, i);
				}

				// Apply direct lighting strategy
				switch (lightStrategy) {
					case SAMPLE_ALL_UNIFORM:
						for (u_int i = 0; i < scene.lights.size(); ++i) {
							const SWCSpectrum Ld(EstimateDirect(scene, *(scene.lights[i]), sample, p, n, wo, bsdf,
								lightSample[0], lightSample[1], lightNum, bsdfSample[0], bsdfSample[1], bsdfComponent));
							if (!Ld.Black()) {
								L[scene.lights[i]->group] += invsamples * Ld;
								++nrContribs;
							}
							// TODO add bsdf selection flags
						}
						break;
					case SAMPLE_ONE_UNIFORM:
					{
						SWCSpectrum Ld;
						u_int g = UniformSampleOneLight(scene, sample, p, n, wo, bsdf,
							lightSample, &lightNum, bsdfSample, &bsdfComponent, &Ld);
						if (!Ld.Black()) {
							L[g] += invsamples * Ld;
							++nrContribs;
						}
						break;
					}
					default:
						break;
				}
			}
		}

		// trace Diffuse reflection & transmission rays
		if (rayDepth < diffuseReflectDepth) {
			ComputeEvent(scene, sample, bsdf, wo, diffuseReflectSamples,
				indirectDiffuseReflectSampleOffset, indirectDiffuseReflectComponentOffset,
				diffuseReflectSampleOffset, diffuseReflectComponentOffset,
				BxDFType(BSDF_REFLECTION | BSDF_DIFFUSE),
				diffuseReflectReject, diffuseReflectRejectThreshold,
				L, alpha, zdepth, rayDepth, nrContribs);
		}
		if (rayDepth < diffuseRefractDepth) {
			ComputeEvent(scene, sample, bsdf, wo, diffuseRefractSamples,
				indirectDiffuseRefractSampleOffset, indirectDiffuseRefractComponentOffset,
				diffuseRefractSampleOffset, diffuseRefractComponentOffset,
				BxDFType(BSDF_TRANSMISSION | BSDF_DIFFUSE),
				diffuseRefractReject, diffuseRefractRejectThreshold,
				L, alpha, zdepth, rayDepth, nrContribs);
		}

		// trace Glossy reflection & transmission rays
		if (rayDepth < glossyReflectDepth) {
			ComputeEvent(scene, sample, bsdf, wo, glossyReflectSamples,
				indirectGlossyReflectSampleOffset, indirectGlossyReflectComponentOffset,
				glossyReflectSampleOffset, glossyReflectComponentOffset,
				BxDFType(BSDF_REFLECTION | BSDF_GLOSSY),
				glossyReflectReject, glossyReflectRejectThreshold,
				L, alpha, zdepth, rayDepth, nrContribs);
		}
		if (rayDepth < glossyRefractDepth) {
			ComputeEvent(scene, sample, bsdf, wo, glossyRefractSamples,
				indirectGlossyRefractSampleOffset, indirectGlossyRefractComponentOffset,
				glossyRefractSampleOffset, glossyRefractComponentOffset,
				BxDFType(BSDF_TRANSMISSION | BSDF_GLOSSY),
				glossyRefractReject, glossyRefractRejectThreshold,
				L, alpha, zdepth, rayDepth, nrContribs);
		}
		
		// trace specular reflection & transmission rays
		if (rayDepth < specularReflectDepth) {
			float pdf;
			Vector wi;
			SWCSpectrum f;
			if (bsdf->SampleF(sw, wo, &wi, .5f, .5f, .5f, &f, &pdf,
				BxDFType(BSDF_REFLECTION | BSDF_SPECULAR),
				NULL, NULL, true)) {
				Ray rd(p, wi);
				rd.time = time;
				vector<SWCSpectrum> Ll(L.size(),
					SWCSpectrum(0.f));
				LiInternal(scene, sample, bsdf->GetVolume(wi),
					bsdf->dgShading.scattered, rd, Ll,
					alpha, zdepth, rayDepth + 1, true,
					nrContribs);
				for (u_int j = 0; j < L.size(); ++j)
					L[j] += f * Ll[j];
			}
		}
		if (rayDepth < specularRefractDepth) {
			float pdf;
			Vector wi;
			SWCSpectrum f;
			if (bsdf->SampleF(sw, wo, &wi, .5f, .5f, .5f, &f, &pdf,
				BxDFType(BSDF_TRANSMISSION | BSDF_SPECULAR),
				NULL, NULL, true)) {
				Ray rd(p, wi);
				rd.time = time;
				vector<SWCSpectrum> Ll(L.size(),
					SWCSpectrum(0.f));
				LiInternal(scene, sample, bsdf->GetVolume(wi),
					bsdf->dgShading.scattered, rd, Ll,
					alpha, zdepth, rayDepth + 1, true,
					nrContribs);
				for (u_int j = 0; j < L.size(); ++j)
					L[j] += f * Ll[j];
			}
		}
	} else {
		// Handle ray with no intersection
		BSDF *ibsdf;
		for (u_int i = 0; i < scene.lights.size(); ++i) {
			SWCSpectrum Le(1.f);
			if (scene.lights[i]->Le(scene, sample, ray, &ibsdf,
				NULL, NULL, &Le)) {
				L[scene.lights[i]->group] += Le;
				++nrContribs;
			}
		}
		if (rayDepth == 0)
			*alpha = 0.f;
	}
	Lt /= spdf;

	for (u_int i = 0; i < L.size(); ++i)
		L[i] *= Lt;
	SWCSpectrum Lv(0.f);
	u_int g = scene.volumeIntegrator->Li(scene, ray, sample, &Lv, alpha);
	L[g] += Lv;
}