void AmbientOcclusionManager::setTextureSize(AOTextureSize textureSize)
	{
		this->textureSize = textureSize;

		createOrUpdateAOTexture();
		createOrUpdateAOShader();
	}
	void AmbientOcclusionManager::setNumSteps(unsigned int numSteps)
	{
		this->numSteps = numSteps;

		createOrUpdateAOTexture();
		createOrUpdateAOShader();
	}
	void AmbientOcclusionManager::setKernelSamples(unsigned int kernelSamples)
	{
		this->kernelSamples = kernelSamples;

		createOrUpdateAOTexture();
		createOrUpdateAOShader();
	}
	void AmbientOcclusionManager::onCameraProjectionUpdate(const Camera *const camera)
	{
		nearPlane = camera->getNearPlane();
		farPlane = camera->getFarPlane();
		projectionScale = camera->getProjectionMatrix()(1, 1);

		createOrUpdateAOTexture();
		createOrUpdateAOShader();
	}
	void AmbientOcclusionManager::onResize(unsigned int width, unsigned int height)
	{
		sceneWidth = width;
		sceneHeight = height;
		
		createOrUpdateAOTexture();
		createOrUpdateAOShader();

		ShaderManager::instance()->bind(hbaoShader);
		Vector2<float> invResolution(1.0f/sceneWidth, 1.0f/sceneHeight);
		glUniform2fv(invResolutionLoc, 1, invResolution);
	}
	AmbientOcclusionManager::AmbientOcclusionManager(unsigned int depthTexID, unsigned int normalAndAmbientTexID) :
		sceneWidth(0),
		sceneHeight(0),
		nearPlane(0.0f),
		farPlane(0.0f),
		projectionScale(0.0f),

		textureSize(DEFAULT_TEXTURE_SIZE),
		textureSizeX(0),
		textureSizeY(0),
        kernelSamples(DEFAULT_KERNEL_SAMPLES),
		radius(DEFAULT_RADIUS),
		ambientOcclusionStrength(DEFAULT_AO_STRENGTH),
		depthStartAttenuation(DEFAULT_DEPTH_START_ATTENUATION),
		depthEndAttenuation(DEFAULT_DEPTH_END_ATTENUATION),
		noiseTextureSize(DEFAULT_NOISE_TEXTURE_SIZE),
		bias(DEFAULT_BIAS),
		blurSize(DEFAULT_BLUR_SIZE),
		blurSharpness(DEFAULT_BLUR_SHARPNESS),


		fboID(0),
		ambientOcclusionTexID(0),

		ambientOcclusionShader(0),
		mInverseViewProjectionLoc(0),
        mProjectionLoc(0),
		mViewLoc(0),
        resolutionLoc(0),
		noiseTexId(0),

		depthTexID(depthTexID),
		normalAndAmbientTexID(normalAndAmbientTexID),
		ambientOcclusionTexLoc(0),
		verticalBlurFilter(nullptr),
		horizontalBlurFilter(nullptr),
		isBlurActivated(true)
	{
		glBindTexture(GL_TEXTURE_2D, depthTexID);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		glBindTexture(GL_TEXTURE_2D, normalAndAmbientTexID);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		quadDisplayer = std::make_unique<QuadDisplayerBuilder>()->build();

		//frame buffer object
		glGenFramebuffers(1, &fboID);
		createOrUpdateAOTexture();
		createOrUpdateAOShader();
	}
	void AmbientOcclusionManager::onResize(unsigned int width, unsigned int height)
	{
		sceneWidth = width;
		sceneHeight = height;
		
		createOrUpdateAOTexture();
		createOrUpdateAOShader();

		ShaderManager::instance()->bind(ambientOcclusionShader);
        Vector2<float> resolution(sceneWidth, sceneHeight);
        glUniform2fv(resolutionLoc, 1, (const float *)resolution);
	}
	AmbientOcclusionManager::AmbientOcclusionManager(unsigned int depthTexID, unsigned int normalAndAmbientTexID) :
		sceneWidth(0),
		sceneHeight(0),
		nearPlane(0.0f),
		farPlane(0.0f),
		projectionScale(0.0f),

		textureSize((AOTextureSize)DEFAULT_TEXTURE_SIZE),
		textureSizeX(0),
		textureSizeY(0),
		numDirections(DEFAULT_NUM_DIRECTIONS),
		numSteps(DEFAULT_NUM_STEPS),
		radius(DEFAULT_RADIUS),
		ambientOcclusionExponent(DEFAULT_AO_EXPONENT),
		biasAngleInDegree(DEFAULT_BIAS_ANGLE_IN_DEGREE),
		blurSize(DEFAULT_BLUR_SIZE),
		blurSharpness(DEFAULT_BLUR_SHARPNESS),
		randomTextureSize(RANDOM_TEXTURE_SIZE),

		fboID(0),
		ambientOcclusionTexID(0),

		hbaoShader(0),
		mInverseViewProjectionLoc(0),
		cameraPlanesLoc(0),
		invResolutionLoc(0),
		nearPlaneScreenRadiusLoc(0),
		randomTexID(0),

		depthTexID(depthTexID),
		normalAndAmbientTexID(normalAndAmbientTexID),
		ambienOcclusionTexLoc(0),
		verticalBlurFilter(nullptr),
		horizontalBlurFilter(nullptr),
		isBlurActivated(true)
	{
		glBindTexture(GL_TEXTURE_2D, depthTexID);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		glBindTexture(GL_TEXTURE_2D, normalAndAmbientTexID);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

		quadDisplayer = std::make_unique<QuadDisplayerBuilder>()->build();

		//frame buffer object
		glGenFramebuffers(1, &fboID);
		createOrUpdateAOTexture();
		createOrUpdateAOShader();
	}
	void AmbientOcclusionManager::setBlurSharpness(float blurSharpness)
	{
		this->blurSharpness = blurSharpness;

		createOrUpdateAOTexture();
	}
	void AmbientOcclusionManager::setBlurSize(unsigned int blurSize)
	{
		this->blurSize = blurSize;

		createOrUpdateAOTexture();
	}