Mat44f CDirectionalLight::GetLightProjectionMatrix() const
{
  D3DXMATRIX l_LightProjectionMatrix;
	D3DXMatrixPerspectiveFovLH( &l_LightProjectionMatrix, 60.0f*D3DX_PI/180.0f, 1.0f, 1.0f, 1000.0f );

  return Mat44f(l_LightProjectionMatrix);
}
Mat44f CDirectionalLight::GetLightViewMatrix()  const
{
  D3DXMATRIX l_LightViewMatrix;
  D3DXVECTOR3 l_Eye(m_Position.x, m_Position.y, m_Position.z), 
              l_LookAt(m_Position.x + m_Direction.x*10, m_Position.y + m_Direction.y*10, m_Position.z + m_Direction.z*10), 
							l_VUP(0.0f,1.0f,0.0f);
  D3DXMatrixLookAtLH( &l_LightViewMatrix, &l_Eye, &l_LookAt, &l_VUP);
  return Mat44f(l_LightViewMatrix);
}
void SphereCapLight::getIrradianceSamples(Vec3f point, const Scene* scene, vector<LightRay>& result, float time) {
	// Calculate cap heigth
	float connectionSq = (point - location).length2();
	float connection = sqrt(connectionSq);
	float tangentSq = connectionSq - radius*radius;
	float heightToPoint = tangentSq / connection;
	auto sphereWarping = std::unique_ptr<Warping>(new UniformSphereCapWarping((radius - (connection-heightToPoint)) / radius));

	// Calculate rotation
	Vec3f dir = (point - location).normalized();
	Mat44f rotation = Mat44f(1);
	rotation.rotateTo(Vec3f(0,0,1), dir);

	// Draw new samples
	std::vector<Vec2f> drawnPoints(nSamples);
	randomSampler->generateSamples(nSamplesSqrt, drawnPoints);
	
	Color3f sum = Color3f(0);
	for (unsigned int i = 0; i < nSamples; i++) {
		// Warp and rotate samples
		if (point.x < 0 && point.y > 0)
			int a = 0;
		Vec3f warpedPoint = rotation * sphereWarping->warp(drawnPoints[i]);
		Vec3f spherePoint = warpedPoint * radius;

		Vec3f difference = point - (spherePoint + location);
		float distance = difference.length();

		LightRay lr;
		Ray r;
		lr.direction = difference / distance;
		r.d = lr.direction;
		r.o = spherePoint + location;
		r.tMax = distance - r.tMin;
		r.time = time;

		// Check for intersection
		for (unsigned int k = 0; k < scene->shapes.size(); k++) {
			scene->shapes[k]->intersect(&r);
		}

		//if ray hit something then it does not contribute
		if (r.hit.shape == 0) {
			float pdf = sphereWarping->pdf(drawnPoints[i]);
			//lr.radiance = power * dot(lr.direction, warpedPoint) / (sphereWarping->pdf(drawnPoints[i]) * 4*PI * pow(distance+radius, 2));
			float a = 1/(sphereWarping->pdf(drawnPoints[i]) * 4*PI);
			lr.radiance = radiance * 4 * pow(radius,2) * std::max(0.f,dot(lr.direction, warpedPoint)) / (sphereWarping->pdf(drawnPoints[i]) * 4*PI * pow(distance, 2));
		}
		else {
			lr.radiance = Color3f(0);
		}
		result.push_back(lr);
	}
}
Color3f AmbientOcclusionShader::shade(const HitInfo & hit, const Scene* scene, stack<float>& refractionIndices) const {
	// Draw new samples
	std::vector<Vec2f> drawnPoints(nSamples);
	currentSampler->generateSamples(nSamplesSqrt, drawnPoints);
	
	// Calculate rotation
	Mat44f rotation = Mat44f(1);
	rotation.rotateTo(Vec3f(0,0,1), hit.N);

	Color3f sum = Color3f(0);
	for (size_t i = 0; i < nSamples; i++) {
		Vec3f warpedPoint;
		// Include the environment Map weighting
		if (environmentMapWeighted) {
			warpedPoint = scene->background->importanceSample(drawnPoints[i]);
			while (dot(warpedPoint, hit.N) < 0) {
				std::vector<Vec2f> sample(1);
				currentSampler->generateSamples(1, sample);
				warpedPoint = scene->background->importanceSample(sample.front());
			}
		}
		else {
			// Warp and rotate samples
			warpedPoint = rotation * currentWarping->warp(drawnPoints[i]);
		}
		Ray r(hit.P, warpedPoint);
		r.time = hit.time;

		// Check for intersection
		for (unsigned int k = 0; k < scene->shapes.size(); k++) {
			scene->shapes[k]->intersect(&r);
		}

		//if ray hit something then it does not contribute
		if (r.hit.shape == 0) {
			if (cosineWeighted)
				sum += Vec3f(scene->background->getBackground(r.d).toArray());
			else
				sum += Vec3f(scene->background->getBackground(r.d).toArray()) * dot(warpedPoint, hit.N);
		}
	}

	if (!cosineWeighted)
		sum *= 2;

	return sum / static_cast<float>(nSamples);
}