#include "RayTracer.h"
#include "WhittedRenderer.h"
#include "PathRenderer.h"
#include "VRUT_NsightCustomEvents.h"
using namespace VRUT;

#ifdef USE_TEMPORAL
void RayTracer::resize() 
{
	vector<OglTexTarget*> textars;
	for (int i = 0; i < 3; i++) 
	{
		textars.clear();

		texReproj[i].intFormat = GL_RGBA16F;
		texReproj[i].format = GL_RGBA;
		texReproj[i].type = GL_FLOAT;
		texReproj[i].magFilter = GL_NEAREST;
		texReproj[i].minFilter = GL_NEAREST;
		texReproj[i].clampBorder = true;

		texAlbedo[i].intFormat = GL_RGBA16F;
		texAlbedo[i].format = GL_RGBA;
		texAlbedo[i].type = GL_FLOAT;
		texAlbedo[i].magFilter = GL_NEAREST;
		texAlbedo[i].minFilter = GL_NEAREST;
		texAlbedo[i].clampBorder = true;

		texND[i].intFormat = GL_RGBA16F;
		texND[i].format = GL_RGBA;
		texND[i].type = GL_FLOAT;
		texND[i].magFilter = GL_NEAREST;
		texND[i].minFilter = GL_NEAREST;
		texND[i].clampBorder = true;

		texVar[i].intFormat = GL_RGBA16F;
		texVar[i].format = GL_RGBA;
		texVar[i].type = GL_FLOAT;
		texVar[i].magFilter = GL_NEAREST;
		texVar[i].minFilter = GL_NEAREST;
		texVar[i].clampBorder = true;

		tex3D[i].intFormat = GL_RGBA32F;
		tex3D[i].format = GL_RGBA;
		tex3D[i].type = GL_FLOAT;
		tex3D[i].magFilter = GL_NEAREST;
		tex3D[i].minFilter = GL_NEAREST;
		tex3D[i].clampBorder = true;

		/*texReprojM[i].intFormat = GL_RGBA16F;
		texReprojM[i].format = GL_RGBA;
		texReprojM[i].type = GL_FLOAT;
		texReprojM[i].magFilter = GL_NEAREST;
		texReprojM[i].minFilter = GL_NEAREST;
		texReprojM[i].clampBorder = true;*/

		/*texVarM[i].intFormat = GL_RGBA16F;
		texVarM[i].format = GL_RGBA;
		texVarM[i].type = GL_FLOAT;
		texVarM[i].magFilter = GL_NEAREST;
		texVarM[i].minFilter = GL_NEAREST;
		texVarM[i].clampBorder = true;*/

		/*texReprojView.intFormat = GL_RGBA16F;
		texReprojView.format = GL_RGBA;
		texReprojView.type = GL_FLOAT;
		texReprojView.magFilter = GL_NEAREST;
		texReprojView.minFilter = GL_NEAREST;
		texReprojView.clampBorder = true;

		texVarView.intFormat = GL_RGBA16F;
		texVarView.format = GL_RGBA;
		texVarView.type = GL_FLOAT;
		texVarView.magFilter = GL_NEAREST;
		texVarView.minFilter = GL_NEAREST;
		texVarView.clampBorder = true;*/

		textars.push_back(&texReproj[i]);
		textars.push_back(&texAlbedo[i]);
		textars.push_back(&texND[i]);
		textars.push_back(&texVar[i]);
		textars.push_back(&tex3D[i]);
		/*textars.push_back(&texReprojM[i]);
		textars.push_back(&texVarM[i]);*/
		/*textars.push_back(&texReprojView);
		textars.push_back(&texVarView);*/

		bufferReproj[i].init(textars, 0, texWidth, texHeight);
	}

	for (int i = 0; i < 2; i++) {
		reprojDepth[i].intFormat = GL_R32UI;
		reprojDepth[i].format = GL_RED_INTEGER;
		reprojDepth[i].type = GL_UNSIGNED_INT;
		reprojDepth[i].magFilter = GL_NEAREST;
		reprojDepth[i].minFilter = GL_NEAREST;
		reprojDepth[i].clampBorder = true;

		//GLenum err = glGetError();
		reprojDepth[i].init(texWidth, texHeight, 0);

		//err = glGetError();

		reprojUV[i].intFormat = GL_RGBA16F;
		reprojUV[i].format = GL_RGBA;
		reprojUV[i].type = GL_FLOAT;
		reprojUV[i].magFilter = GL_NEAREST;
		reprojUV[i].minFilter = GL_NEAREST;
		reprojUV[i].clampBorder = true;
		reprojUV[i].init(texWidth, texHeight, 0);
	}

	int coeff[] = { 3,9,27,1 };
	for (int i = 0; i < 4; i++) 
	{
		for (int ii = 0; ii < 2; ii++) 
		{
			if (ii && i > 1)
				continue;

			//nornals, depth
			texNDF[ii][i].intFormat = GL_RGBA32F;
			texNDF[ii][i].format = GL_RGBA;
			texNDF[ii][i].type = GL_FLOAT;
			texNDF[ii][i].magFilter = GL_NEAREST;
			texNDF[ii][i].minFilter = GL_NEAREST;
			if (i == 2 || ii || (i == 3 && i == 0)) {
				texNDF[ii][i].magFilter = GL_LINEAR;
				texNDF[ii][i].minFilter = GL_LINEAR;
			}
			texNDF[ii][i].clampBorder = true;

			texAlbedoF[ii][i].intFormat = GL_RGBA32F;
			texAlbedoF[ii][i].format = GL_RGBA;
			texAlbedoF[ii][i].type = GL_FLOAT;
			texAlbedoF[ii][i].magFilter = GL_NEAREST;
			texAlbedoF[ii][i].minFilter = GL_NEAREST;
			if (i == 2 || ii) {
				texAlbedoF[ii][i].magFilter = GL_LINEAR;
				texAlbedoF[ii][i].minFilter = GL_LINEAR;
			}
			texAlbedoF[ii][i].clampBorder = true;

			textars.clear();
			textars.push_back(&texNDF[ii][i]);
			textars.push_back(&texAlbedoF[ii][i]);
			bufferND[ii][i].init(textars, 0, texWidth / coeff[i], texHeight / coeff[i]);

			/*
			//reproj uint depth
			texDMin[ii][i].intFormat = GL_R32UI;
			texDMin[ii][i].format = GL_RED_INTEGER;
			texDMin[ii][i].type = GL_UNSIGNED_INT;
			texDMin[ii][i].magFilter = GL_NEAREST;
			texDMin[ii][i].minFilter = GL_NEAREST;
			texDMin[ii][i].clampBorder = true;

			textars.clear();
			textars.push_back(&texDMin[ii][i]);
			bufferDMin[ii][i].init(textars, 0, texWidth, texHeight);*/
		}
	}

	for (int i = 0; i < 3; i++) {
		//reproj uint depth
		texDMin[i].intFormat = GL_RG32UI;
		texDMin[i].format = GL_RG_INTEGER;
		texDMin[i].type = GL_UNSIGNED_INT;
		texDMin[i].magFilter = GL_NEAREST;
		texDMin[i].minFilter = GL_NEAREST;
		texDMin[i].clampBorder = true;

		textars.clear();
		textars.push_back(&texDMin[i]);
		bufferDMin[i].init(textars, 0, texWidth, texHeight);
	}

	for (int i = 0; i < 2; i++) {
		//reproj uint depth
		texDMax[i].intFormat = GL_R32UI;
		texDMax[i].format = GL_RED_INTEGER;
		texDMax[i].type = GL_UNSIGNED_INT;
		texDMax[i].magFilter = GL_NEAREST;
		texDMax[i].minFilter = GL_NEAREST;
		texDMax[i].clampBorder = true;

		textars.clear();
		textars.push_back(&texDMax[i]);
		bufferDMax[i].init(textars, 0, texWidth, texHeight);
	}

	for (int i = 0; i < 2; i++) {
		textars.clear();
		texVarBlur[i].intFormat = GL_RGBA16F;
		texVarBlur[i].format = GL_RGBA;
		texVarBlur[i].type = GL_FLOAT;
		texVarBlur[i].magFilter = GL_NEAREST;
		texVarBlur[i].minFilter = GL_NEAREST;
		texVarBlur[i].clampBorder = true;

		textars.push_back(&texVarBlur[i]);
		bufferVarBlur[i].init(textars, 0, texWidth, texHeight);
	}

	textars.clear();
	texWaveletMemory.intFormat = GL_RGBA16F;
	texWaveletMemory.format = GL_RGBA;
	texWaveletMemory.type = GL_FLOAT;
	texWaveletMemory.magFilter = GL_NEAREST;
	texWaveletMemory.minFilter = GL_NEAREST;
	texWaveletMemory.clampBorder = true;

	textars.push_back(&texWaveletMemory);
	bufferWaveletMemory.init(textars, 0, texWidth, texHeight);

	for (int i = 0; i < WAVELEVELS; i++) {
		textars.clear();
		texWavelet[i].intFormat = GL_RGBA16F;
		texWavelet[i].format = GL_RGBA;
		texWavelet[i].type = GL_FLOAT;
		texWavelet[i].magFilter = GL_NEAREST;
		texWavelet[i].minFilter = GL_NEAREST;
		//if (!i) {
		//	texWavelet[i].magFilter = GL_NEAREST;
		//	texWavelet[i].minFilter = GL_NEAREST;
		//}
		texWavelet[i].clampBorder = true;

		textars.push_back(&texWavelet[i]);
		bufferWavelet[i].init(textars, 0, texWidth, texHeight);
	}

	for (int i = 0; i < 2; i++) 
	{
		textars.clear();
		texTemp[i].intFormat = GL_RGBA16F;
		texTemp[i].format = GL_RGBA;
		texTemp[i].type = GL_FLOAT;
		texTemp[i].magFilter = GL_NEAREST;
		texTemp[i].minFilter = GL_NEAREST;
		texTemp[i].clampBorder = true;

		textars.push_back(&texTemp[i]);
		bufferTemp[i].init(textars, 0, texWidth, texHeight);
	}

	GLCHECKERROR(wxT("RayTracer::buffer "));
}

void RayTracer::initGl() 
{
	//hack to speedup glDrawPixels from http://www.cs.rit.edu/~ncs/Courses/570/UserGuide/OpenGLonWin-31.html
	glDisable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_DITHER);
	glDisable(GL_STENCIL_TEST);

	resize();
	nowTex = 0; frame = 0;
	camNotMovedCnt = 0;

	//prepare for QUAD drawing
	for (int tex = 0; tex < NUM_TEMPORAL_TEXTURES; tex++) {
		texCPU[tex].init(texWidth, texHeight);
	}

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

#if PBO_COUNT > 0
	for (int tex = 0; tex < NUM_TEMPORAL_TEXTURES; tex++)
		glGenBuffers(PBO_COUNT, pboIds[tex]);
#endif
#if USE_GL_FILTER
	wxString shaderPrefix = KERNEL->GetAppPath() + wxT("modules/render/");
	//wxString shaderPrefix = wxT("../base_modules/RayTracer/shaders/");
	
	shV.compile(shaderPrefix + "rt_filter.vert");
	shF.compile(shaderPrefix + "rt_filter.frag");
	shProg.link(shV, shF);

	shVQuad.compile(shaderPrefix + "Quad.vsh");
	shFQuad.compile(shaderPrefix + "Quad.fsh");
	shProgQuad.link(shVQuad, shFQuad);

	shFReproject.compile(shaderPrefix + "Reproject.fsh");
	shCNewProjectDepth.compile(shaderPrefix + "NewProjectDepth.csh");
	shCNewProjectUV.compile(shaderPrefix + "NewProjectUV.csh");
	shCNegativeClear.compile(shaderPrefix + "NegativeClear.csh");

	shProgReproject.link(shVQuad, shFReproject);
	shProgNewProjectDepth.linkC(shCNewProjectDepth);
	shProgNewProjectUV.linkC(shCNewProjectUV);
	shProgNegativeClear.linkC(shCNegativeClear);

	shFReduceND.compile(shaderPrefix + "ReduceND.fsh");
	shProgReduceND.link(shVQuad, shFReduceND);
	shFReduceDMin.compile(shaderPrefix + "ReduceDMin.fsh");
	shProgReduceDMin.link(shVQuad, shFReduceDMin);
	/*shFReduceDMax.compile(shaderPrefix + "ReduceDMax.fsh");
	shProgReduceDMax.link(shVQuad, shFReduceDMax);*/

	shFVarBlur.compile(shaderPrefix + "VarBlur.fsh");
	shProgVarBlur.link(shVQuad, shFVarBlur);

	shFExpandND.compile(shaderPrefix + "ExpandND.fsh");
	shProgExpandND.link(shVQuad, shFExpandND);
	/*shFExpandDMin.compile(shaderPrefix + "ExpandDMin.fsh");
	shProgExpandDMin.link(shVQuad, shFExpandDMin);*/

	shFCopyBuffers.compile(shaderPrefix + "CopyBuffers.fsh");
	shProgCopyBuffers.link(shVQuad, shFCopyBuffers);

	shFWavelet.compile(shaderPrefix + "WaveletBlur.fsh");
	shProgWavelet.link(shVQuad, shFWavelet);

	shFTemp.compile(shaderPrefix + "TempAA.fsh");
	shProgTemp.link(shVQuad, shFTemp);

	shProg.enable();
	shProg.importValue("filterRadius", (float)filterRadius);
	shProg.importValue("frameId", (unsigned int)0);
	shProg.importValue("keyValue", (float)((float)toneMappingKeyValue) * 1.0e-2f);
	shProg.importValue("whitePoint", (float)((float)toneMappingWhitePoint) * 1.0e-2f);
	shProg.importValue("texDownscaleRatio", 1.0f);
	shProg.importValue("imageTexture", &texCPU[0]);
	shProg.importValue("resolution", Vector2(1, 1));

#else
	shader = nullptr;
#endif

	initQuadData();

	GLCHECKERROR(wxT("RayTracer::Initialize "));

	wxString oglVendor(wxString::FromAscii((char*)glGetString(GL_VENDOR)));
	if (oglVendor.Find(wxT("ATI")) != wxNOT_FOUND || oglVendor.Find(wxT("AMD")) != wxNOT_FOUND)
		openGLVendor = AMD;

}

void RayTracer::renderGl()
{
	if (useTemporalPathTracing) 
	{
		VRUT::NODE_ID id = *scene->GetNodeIDs("default_camera").begin();
		Matrix m0(-0.93711, 0.011107, -0.34879, 0, -0.048100, 0.985813, 0.160637, 0, 0.345630, 0.167314, -0.92329, 0, -1113.25, 199.3845, -432.169, 1);
		
		//epic hack for test scene setup
		if (frame < 2) {
			rerender = true;
			camMoved = true;
			renderer->SetHeadlight(Vector4(0,0,0,0));

			int mId = scene->GetMaterialID("wire_028089177");
			int nId = *((scene->GetNodeIDs("Sphere001")).begin());
			const Vector4 eMat(1, 1, 1, 1);
			Material *newMat = new Material("hu");
			newMat->emission = eMat;

			int mId2 = scene->AddMaterial(newMat);
			scene->SetMaterial(nId, mId2);
		}

#define MN 2
		Matrix m[MN+1];
		m[0] = Matrix(-0.93711, 0.011107, -0.34879, 0, -0.048100, 0.985813, 0.160637, 0, 0.345630, 0.167314, -0.92329, 0, 
			-1270.294, 206.5216, -489.656, 1);
		m[1] = Matrix(-0.93711, 0.011107, -0.34879, 0, -0.048100, 0.985813, 0.160637, 0, 0.345630, 0.167314, -0.92329, 0, 
			-1040.81, 184.2847, -407.7883, 1);
		m[2] = m[0];

		float ratio = 1.0f*frame / 100, ratio2;
		ratio -= floor(ratio);
		int start = 0, end = 1;
		for (int i = 0; i < MN; i++) {
			if (ratio >= 1.0f*i / MN && ratio < 1.0f*(i + 1) / MN) {
				ratio2 = (ratio - 1.0f*i / MN)/(1.0f / MN);
				start = i;
				end = i + 1;
			}
		}

		Matrix mm = m[start]*ratio2 + m[end]*(1 - ratio2);
		scene->SetTransformation(id, m0);
		//camMoved = 1;
		//rerender = 1;

		//MVPprev = cameras[0]->viewProjectionInverse.Inverse(); //stupid
		MVP[2] = MVP[1];
		MVPinv[2] = MVPinv[1];
		origin[2] = origin[1];

		MVP[1] = MVP[0];
		MVPinv[1] = MVPinv[0];
		origin[1] = origin[0];




		



		//camMoved = 1;
		//MVP = cameras[0]->viewProjectionInverse.Inverse(); //stupid
		MVP[0] = cameras[0]->cameraWorldMatrix.Inverse() * cameras[0]->projectionMatrix;
		MVPinv[0] = cameras[0]->viewProjectionInverse;
		origin[0] = cameras[0]->origin;

		bindCPUTexture(0);
		subImage2D();

		bindCPUTexture(1);
		subImage2D();

		bindCPUTexture(2);
		subImage2D();




		GLuint qid;
		glGenQueries(1, &qid);
		glBeginQuery(GL_TIME_ELAPSED, qid);


		

		//if (useReproject) {
			//doNewProject(0, !nowTex, nowTex);
			//doNewProject(1, 2, nowTex);
		//}

		bufferReproj[nowTex].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepReproject();
		DrawFullscreenQuad();



		//reduce
		/*bufferND[0][0].enable();
		glViewport(0, 0, texWidth / 3, texHeight / 3);
		prepReduceND(&texND[nowTex], &texAlbedo[nowTex], 1);
		DrawFullscreenQuad();

		bufferND[0][1].enable();
		glViewport(0, 0, texWidth / 9, texHeight / 9);
		prepReduceND(&texNDF[0][0], &texAlbedoF[0][0], 3);
		DrawFullscreenQuad();

		bufferND[0][2].enable();
		glViewport(0, 0, texWidth / 27, texHeight / 27);
		prepReduceND(&texNDF[0][1], &texAlbedoF[0][1], 9);
		DrawFullscreenQuad();

		//expand
		bufferND[1][1].enable();
		glViewport(0, 0, texWidth / 9, texHeight / 9);
		prepExpandND(&texNDF[0][2], &texNDF[0][1], &texAlbedoF[0][2], &texAlbedoF[0][1]);
		DrawFullscreenQuad();

		bufferND[1][0].enable();
		glViewport(0, 0, texWidth / 3, texHeight / 3);
		prepExpandND(&texNDF[1][1], &texNDF[0][0], &texAlbedoF[1][1], &texAlbedoF[0][0]);
		DrawFullscreenQuad();

		bufferND[0][3].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepExpandND(&texNDF[1][0], &texND[nowTex], &texAlbedoF[1][0], &texAlbedo[nowTex]);
		DrawFullscreenQuad();*/




		bufferVarBlur[0].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepVarBlur(&texVar[nowTex],1);
		DrawFullscreenQuad();

		//bufferVarBlur[1].enable();
		//glViewport(0, 0, texWidth, texHeight);
		//prepVarBlur(&texVarBlur[0],2);
		//DrawFullscreenQuad();



		float time = wxGetLocalTimeMillis().ToDouble();
		bufferWavelet[0].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepWavelet(&texVarBlur[0], &texNDF[0][3], 0, time);
		DrawFullscreenQuad();

		for (int i = 0; i < WAVELEVELS - 1; i++) {
			bufferWavelet[i + 1].enable();
			glViewport(0, 0, texWidth, texHeight);
			prepWavelet(&texWavelet[i], &texNDF[0][3], WAVELEVELS - 1 - i, time);
			DrawFullscreenQuad();
		}

		bufferTemp[nowTex].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepTemp();
		DrawFullscreenQuad();

		/*bufferReproj[2].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepCopyBuffers(!nowTex);
		DrawFullscreenQuad();*/


		if (openVRWrapper != nullptr)
			renderTarget.Bind();
		else
			RenderTarget::Unbind();

		glViewport(0, 0, texWidth, texHeight);
		//prepQuad(&texWavelet[WAVELEVELS - 1]);
		//prepQuad(&texDMin[0][0]);
		//prepQuad(&texAlbedo[nowTex]);
		//prepQuad(&texCPU[0]);
		/*if (!filterRadius)
			prepQuad(&reprojDepth);
		else if (filterRadius == 1)
			prepQuad(&texDMin[0][0]);
		else if (filterRadius == 2)
			prepQuad(&texDMin[0][1]);
		else if (filterRadius == 3)
			prepQuad(&texDMin[0][2]);
		else if (filterRadius == 4)
			prepQuad(&texDMin[1][1]);
		else if (filterRadius == 5)
			prepQuad(&texDMin[1][0]);
		else
			prepQuad(&texDMin[0][3]);*/
		prepQuad(&texTemp[nowTex]);
		//prepQuad(&texVar[nowTex]);
		//prepQuad(&texReproj[nowTex]);
		//prepQuad(&reprojDepth[0]);
		//prepQuad(&tex3D[nowTex]);
		//prepQuad(&texWavelet[0]);

		/*if (filterRadius < 5)
			prepQuad(&texReproj[nowTex]);
		else if (filterRadius < 7)
			prepQuad(&texWavelet[WAVELEVELS - 1]);
		else if (filterRadius == 7)
			prepQuad(&texTemp[nowTex]);
		else if (filterRadius < 10)
			prepQuad(&texND[nowTex]);
		else if (filterRadius == 10)
			prepQuad(&texAlbedo[nowTex]);
		else if (filterRadius == 11)
			prepQuad(&texVarBlur[0]);
		else
			prepQuad(&texWavelet[1]);*/

		DrawFullscreenQuad();

		nowTex = !nowTex;
		frame++;





		//time debugging
		//glFinish();

		glEndQuery(GL_TIME_ELAPSED);
		GLuint64 tyme;
		glGetQueryObjectui64v(qid, GL_QUERY_RESULT, &tyme);
		LOG(wxString::Format(wxT("ms elapsed: %f"), (float)1.0f*tyme/1000000));









		camMovedPrev = camMoved;
		//camMoved = (MVP != MVPprev);
		if (!camMoved)
			camNotMovedCnt++;
		else
			camNotMovedCnt = 0;
		camMoved = false;


		/*bindCPUTexture(0);
		subImage2D();

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		prepFilter(0);
		drawQuad();*/
	}
	else 
	{
		if (openVRWrapper == nullptr)
			RenderTarget::Unbind();
		bindCPUTexture(0);
		subImage2D();

		prepFilter(0);
		DrawFullscreenQuad();

		//nowTex = !nowTex;
		//frame++;
	}

	// Render from FBO to HMD
	if (openVRWrapper != nullptr)
		submitFrameToOpenVR();

#if PBO_COUNT == 2
	for (int i = 0; i < texturesUsed; i++) {
		if (/*!reset &&*/ texData[i])
		{
			PUSH_RANGE("renderGl glMapBuffer", 4);
			if (!glMapBufferRange)
				texData[i] = (float*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
			POP_RANGE;
		}
	}

#endif //PBO_COUNT == 2
}


void RayTracer::prepQuad(int tex) {
	shProgQuad.enable();
	shProgQuad.importValue("tex", &texCPU[tex]);
	shProgQuad.importValue("showAlbedo", showAlbedo);
	//shProgQuad.importValue("leaveProb", 0.0f);
}

void RayTracer::prepQuad(OglTex *tex) {
	shProgQuad.enable();
	shProgQuad.importValue("tex", tex);
	//shProgQuad.importValue("tex2", &reprojDepth[1]);
	shProgQuad.importValue("texAlbedo", &texAlbedoF[0][3]);
	shProgQuad.importValue("showAlbedo", showAlbedo);
	shProgQuad.importValue("resolution", Vector2(w, h));
	shProgQuad.importValue("debug", (float)filterRadius);

	float avgLuminance = renderer->GetAvgLuminance();
	shProgQuad.importValue("keyValue", ((float)toneMappingKeyValue) * 1.0e-2f);
	shProgQuad.importValue("whitePoint", ((float)toneMappingWhitePoint) * 1.0e-2f);
	shProgQuad.importValue("avgLuminance", avgLuminance);

	//shProgQuad.importValue("leaveProb", 0.0f);
}

void RayTracer::doNewProject(int i, int prev, int now) {
	shProgNegativeClear.enable();

	reprojDepth[i].bind(0);
	reprojUV[i].bind(1);
	glDispatchCompute(ceil(w / 16) + 1, ceil(h / 16) + 1, 1);

	shProgNewProjectDepth.enable();
	reprojDepth[i].bind(0);

	shProgNewProjectDepth.importValue("tex", &texReproj[now]);
	shProgNewProjectDepth.importValue("texPrev", &texReproj[prev]);
	shProgNewProjectDepth.importValue("tex3D", &tex3D[prev]);
	shProgNewProjectDepth.importValue("prevND", &texNDF[0][3]);
	shProgNewProjectDepth.importValue("MVPprev", MVPinv[i+1]);
	shProgNewProjectDepth.importValue("originPrev", origin[i+1]);
	shProgNewProjectDepth.importValue("res", Vector2(w, h));
	shProgNewProjectDepth.importValue("MVP", MVP[i]);
	shProgNewProjectDepth.importValue("origin", origin[i]);
	glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
	glDispatchCompute(ceil(w / 16) + 1, ceil(h / 16) + 1, 1);
	glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);




	if (camMoved) {
		int coeffs[] = { 1, 3, 9, 27 };

		//reduce
		bufferDMin[0].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepReduceDMin(&reprojDepth[i], 1);
		DrawFullscreenQuad();

		for (int i = 1; i < 3; i++) {
			bufferDMin[i].enable();
			glViewport(0, 0, texWidth, texHeight);
			prepReduceDMin(&texDMin[i - 1], coeffs[i]);
			DrawFullscreenQuad();
		}

		/*bufferDMax[0].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepReduceDMax(&texDMin[1], 3);
		DrawFullscreenQuad();

		bufferDMax[1].enable();
		glViewport(0, 0, texWidth, texHeight);
		prepReduceDMax(&texDMax[0], 1);
		DrawFullscreenQuad();*/
	}





	shProgNewProjectUV.enable();
	reprojUV[i].bind(0);

	shProgNewProjectUV.importValue("tex", &texReproj[now]);
	shProgNewProjectUV.importValue("texPrev", &texReproj[prev]);
	shProgNewProjectUV.importValue("tex3D", &tex3D[prev]);
	if (camMoved) {
		shProgNewProjectUV.importValue("texDepth", &texDMin[1]);
		shProgNewProjectUV.importValue("texDepthFar", &texDMin[2]);
		/*shProgNewProjectUV.importValue("texDepth", &texDMax[1]);
		shProgNewProjectUV.importValue("texDepthFar", &texDMin[2]);*/
	}
	else
		shProgNewProjectUV.importValue("texDepth", &reprojDepth[i]);
	shProgNewProjectUV.importValue("prevND", &texNDF[0][3]);
	shProgNewProjectUV.importValue("MVPprev", MVPinv[i+1]);
	shProgNewProjectUV.importValue("originPrev", origin[i+1]);
	shProgNewProjectUV.importValue("res", Vector2(w, h));
	shProgNewProjectUV.importValue("MVP", MVP[i]);
	shProgNewProjectUV.importValue("origin", origin[i]);
	shProgNewProjectUV.importValue("debug", (float)filterRadius);
	shProgNewProjectUV.importValue("camMoved", camMoved?int(1):int(0));
	glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
	glDispatchCompute(ceil(w / 16) + 1, ceil(h / 16) + 1, 1);
}

void RayTracer::prepCopyBuffers(int from) {
	shProgCopyBuffers.enable();
	shProgCopyBuffers.importValue("colorIn", &texReproj[from]);
	shProgCopyBuffers.importValue("albedoIn", &texAlbedo[from]);
	shProgCopyBuffers.importValue("NDIn", &texND[from]);
	shProgCopyBuffers.importValue("varIn", &texVar[from]);
	shProgCopyBuffers.importValue("pos3DIn", &tex3D[from]);
}

void RayTracer::prepReproject() {
	shProgReproject.enable();
	shProgReproject.importValue("texDiff", &texCPU[0]);
	shProgReproject.importValue("texAlbedo", &texCPU[1]);
	shProgReproject.importValue("texNormalDepth", &texCPU[2]);

	shProgReproject.importValue("texDiffPrev", &texReproj[!nowTex]);
	
	int filterRatio = TILE_SIZE*TILE_SIZE / 4; //ratio with filtered prev frame
	switch (renderer->samplingStrategy) {
		case MIN_TEMPORAL_STRATEGY:
			filterRatio = TILE_SIZE*TILE_SIZE / 4;
			break;
		case MIN_TEMPORAL_STRATEGY + 1:
			filterRatio = TILE_SIZE*TILE_SIZE / 8;
			break;
		case MIN_TEMPORAL_STRATEGY + 2:
			filterRatio = TILE_SIZE*TILE_SIZE / 16;
			break;
		case MIN_TEMPORAL_STRATEGY + 3:
			filterRatio = TILE_SIZE*TILE_SIZE / 64;
			break;
		case MIN_TEMPORAL_STRATEGY + 4:
			filterRatio = 1;
			break;
	}
	
	if (0 && !(frame % filterRatio) && frame/* && (filterRadius < 16 || filterRadius > 21)*/ ) {
		shProgReproject.importValue("texDiffFilteredPrev", &texWavelet[0]);
		shProgReproject.importValue("fullNew", 1);
	}
	else {
		shProgReproject.importValue("texDiffFilteredPrev", &texReproj[!nowTex]);
		shProgReproject.importValue("fullNew", 0);
	}

	shProgReproject.importValue("texAlbedoPrev", &texAlbedo[!nowTex]);
	shProgReproject.importValue("texNDPrev", &texND[!nowTex]);
	shProgReproject.importValue("texVarPrev", &texVar[!nowTex]);
	shProgReproject.importValue("texUV", &reprojUV[0]);
	shProgReproject.importValue("prev3D", &tex3D[!nowTex]);

	shProgReproject.importValue("rejectNow", (rerender && (!useReproject || !(camMoved))) ? 1 : 0);
	shProgReproject.importValue("reprojSp", 100.0f);
	shProgReproject.importValue("debug", (float)filterRadius);
	shProgReproject.importValue("useReproject", (int)useReproject);
	shProgReproject.importValue("camMoved", (camMoved || (camNotMovedCnt < 20)) ? int(1) : int(0));
	shProgReproject.importValue("accum", IS_TEMPORAL_STRATEGY(renderer->samplingStrategy)?0:1);
	//shProgReproject.importValue("rejectNow", 1);

	shProgReproject.importValue("texDepth", &texDMin[1]);
	shProgReproject.importValue("texDepthFar", &texDMin[2]);

	shProgReproject.importValue("MVP", MVPinv[0]);
	shProgReproject.importValue("MVPprev", MVP[1]);
	shProgReproject.importValue("origin", origin[0]);
	shProgReproject.importValue("originPrev", origin[1]);

	if (rerender)
		int u = 0;

	/*if (filterRadius < 0)
	{
		// automatic filter radius
		float avgSamples = renderer->GetAvgSamples();
		float radius = 3;
		if ((avgSamples >= 1))
		{
			radius = avgSamples * std::abs(filterRadius) + 2;
			LOG(wxString::Format(wxT("radius %f"), radius));
		}
		shProgReproject.importValue("filterRadius", radius);
	}
	else
		shProgReproject.importValue("filterRadius", (float)filterRadius);
	float avgLuminance = renderer->GetAvgLuminance();

	shProgReproject.importValue("avgLuminance", avgLuminance);
	shProgReproject.importValue("frameId", (unsigned int)((renderer->frameId >> 1) & 0xFFFF));

	shProgReproject.importValue("keyValue", ((float)toneMappingKeyValue) * 1.0e-2f);
	shProgReproject.importValue("whitePoint", ((float)toneMappingWhitePoint) * 1.0e-2f);
	shProgReproject.importValue("texDownscaleRatio", float(texDownscaleFactor));*/
	shProgReproject.importValue("frameId", (unsigned int)((renderer->frameId >> 1) & 0xFFFF));
	shProgReproject.importValue("resolution", Vector2(w, h));
}

void RayTracer::prepReduceND(OglTex *from, OglTex *fromAlbedo, int step) {
	shProgReduceND.enable();
	shProgReduceND.importValue("texNormalDepth", from);
	shProgReduceND.importValue("texAlbedo", fromAlbedo);
	shProgReduceND.importValue("resolution", Vector2(w / step, h / step));
}

void RayTracer::prepReduceDMin(OglTex *from, int step) {
	shProgReduceDMin.enable();
	shProgReduceDMin.importValue("texDepth", from);
	shProgReduceDMin.importValue("resolution", Vector2(w / step, h / step));
}

void RayTracer::prepReduceDMax(OglTex *from, int step) {
	shProgReduceDMax.enable();
	shProgReduceDMax.importValue("texDepth", from);
	shProgReduceDMax.importValue("resolution", Vector2(w / step, h / step));
}

void RayTracer::prepExpandND(OglTex *prev, OglTex *now, OglTex *prevAlbedo, OglTex *nowAlbedo) {
	shProgExpandND.enable();
	shProgExpandND.importValue("texNormalDepthPrev", prev);
	shProgExpandND.importValue("texNormalDepthNow", now);

	shProgExpandND.importValue("texAlbedoPrev", prevAlbedo);
	shProgExpandND.importValue("texAlbedoNow", nowAlbedo);
}

void RayTracer::prepExpandDMin(OglTex *prev, OglTex *now) {
	/*shProgExpandDMin.enable();
	shProgExpandDMin.importValue("texDepthPrev", prev);
	shProgExpandDMin.importValue("texDepthNow", now);*/
}

void RayTracer::prepVarBlur(OglTex *src, int level) {
	shProgVarBlur.enable();
	shProgVarBlur.importValue("texVar", src);
	shProgVarBlur.importValue("tex", &texReproj[nowTex]);
	shProgVarBlur.importValue("texND", &texNDF[0][3]);
	shProgVarBlur.importValue("texAlbedo", &texAlbedoF[0][3]);
	//shProgVarBlur.importValue("level", level);

	shProgVarBlur.importValue("resolution", Vector2(w, h));
}

void RayTracer::prepWavelet(OglTex *src, OglTex *srcND, int l, float time) {
	shProgWavelet.enable();
	shProgWavelet.importValue("tex", src);
	//shProgWavelet.importValue("firstVarTex", &texVar);
	shProgWavelet.importValue("texNormalDepth", srcND);
	shProgWavelet.importValue("texVar", &texVar[nowTex]);
	shProgWavelet.importValue("res", Vector2(w, h));
	shProgWavelet.importValue("time", time);
	shProgWavelet.importValue("iter", (float)pow(2, l));

	float smoothU = smooth / 100.0f;
	if (smoothU > 1)
		smoothU = pow(smoothU, 2);

	shProgWavelet.importValue("smoothU", smoothU);
	shProgWavelet.importValue("reprojSp", 100.0f);
	shProgWavelet.importValue("strength", filterStrength);
	shProgWavelet.importValue("steep", filterSteep);

	float debug = filterRadius;
	shProgWavelet.importValue("debug", debug);

	//if (filterRadius == 22)
	//	shProgWavelet.importValue("debug", 1.0f);
	//else
	//	shProgWavelet.importValue("debug", 0.0f);
}

void RayTracer::prepTemp() {
	shProgTemp.enable();
	shProgTemp.importValue("tex", &texWavelet[WAVELEVELS - 1]);
	shProgTemp.importValue("tex3D", &tex3D[nowTex]);
	shProgTemp.importValue("texReproj", &texReproj[nowTex]);
	shProgTemp.importValue("prevTex", &texTemp[!nowTex]);
	shProgTemp.importValue("texAlbedo", &texAlbedoF[0][3]);
	shProgTemp.importValue("showAlbedo", showAlbedo);
	shProgTemp.importValue("res", Vector2(w, h));
	shProgTemp.importValue("time", (float)wxGetLocalTimeMillis().ToDouble());
	shProgTemp.importValue("rejectNow", rerender || camMoved);

	float avgLuminance = renderer->GetAvgLuminance();
	shProgTemp.importValue("keyValue", ((float)toneMappingKeyValue) * 1.0e-2f);
	shProgTemp.importValue("whitePoint", ((float)toneMappingWhitePoint) * 1.0e-2f);
	shProgTemp.importValue("avgLuminance", avgLuminance);
}

void RayTracer::prepFilter(int tex) {
#if USE_GL_FILTER
	shProg.enable();
	if (filterRadius < 0)
	{
		// automatic filter radius
		float avgSamples = renderer->GetAvgSamples();
		float radius = 3;
		if ((avgSamples >= 1))
		{
			radius = avgSamples * std::abs(filterRadius) + 2;
			LOG(wxString::Format(wxT("radius %f"), radius));
		}
		shProg.importValue("filterRadius", radius);
	}
	else
		shProg.importValue("filterRadius", (float)filterRadius);
	float avgLuminance = renderer->GetAvgLuminance();

	shProg.importValue("avgLuminance", avgLuminance);
	shProg.importValue("frameId", (unsigned int)((renderer->frameId >> 1) & 0xFFFF));

	shProg.importValue("keyValue", ((float)toneMappingKeyValue) * 1.0e-2f);
	shProg.importValue("whitePoint", ((float)toneMappingWhitePoint) * 1.0e-2f);
	shProg.importValue("texDownscaleRatio", float(texDownscaleFactor));
	shProg.importValue("imageTexture", &texCPU[tex]);
	shProg.importValue("resolution", Vector2(w, h));
#endif // USE_GL_FILTER
}

#endif