Exemple #1
0
int main()
{
	////////////////////
	// ionEngine Init //
	////////////////////

	Log::AddDefaultOutputs();

	SingletonPointer<CGraphicsAPI> GraphicsAPI;
	SingletonPointer<CWindowManager> WindowManager;
	SingletonPointer<CTimeManager> TimeManager;
	SingletonPointer<CSceneManager> SceneManager;
	SingletonPointer<CAssetManager> AssetManager;
	SingletonPointer<CGUIManager> GUIManager;

	GraphicsAPI->Init(new Graphics::COpenGLImplementation());
	WindowManager->Init(GraphicsAPI);
	TimeManager->Init(WindowManager);
	SceneManager->Init(GraphicsAPI);
	AssetManager->Init(GraphicsAPI);

	//CWindow * Window = WindowManager->CreateWindow(vec2i(1600, 900), "DemoApplication", EWindowType::Windowed);
	CWindow * Window = WindowManager->CreateWindowOnMonitor(2, "SSAO");

	GUIManager->Init(Window);
	Window->AddListener(GUIManager);

	AssetManager->AddAssetPath("Assets/");
	AssetManager->SetShaderPath("Shaders/");
	AssetManager->SetTexturePath("Images/");

	SharedPointer<IGraphicsContext> Context = GraphicsAPI->GetWindowContext(Window);
	SharedPointer<IRenderTarget> BackBuffer = Context->GetBackBuffer();
	BackBuffer->SetClearColor(color3f(0.3f));

	SharedPointer<IFrameBuffer> SceneFrameBuffer = Context->CreateFrameBuffer();

	SharedPointer<ITexture2D> SceneColor = GraphicsAPI->CreateTexture2D(Window->GetSize(), ITexture::EMipMaps::False, ITexture::EFormatComponents::RGB, ITexture::EInternalFormatType::Fix8);
	SceneColor->SetMinFilter(ITexture::EFilter::Nearest);
	SceneColor->SetMagFilter(ITexture::EFilter::Nearest);
	SceneColor->SetWrapMode(ITexture::EWrapMode::Clamp);
	SharedPointer<ITexture2D> SceneNormal = GraphicsAPI->CreateTexture2D(Window->GetSize(), ITexture::EMipMaps::False, ITexture::EFormatComponents::RGB, ITexture::EInternalFormatType::Fix8);
	SceneNormal->SetMinFilter(ITexture::EFilter::Nearest);
	SceneNormal->SetMagFilter(ITexture::EFilter::Nearest);
	SceneNormal->SetWrapMode(ITexture::EWrapMode::Clamp);
	SharedPointer<ITexture2D> SceneDepth = GraphicsAPI->CreateTexture2D(Window->GetSize(), ITexture::EMipMaps::False, ITexture::EFormatComponents::R, ITexture::EInternalFormatType::Depth);
	SceneFrameBuffer->AttachColorTexture(SceneColor, 0);
	SceneFrameBuffer->AttachColorTexture(SceneNormal, 1);
	SceneFrameBuffer->AttachDepthTexture(SceneDepth);
	if (! SceneFrameBuffer->CheckCorrectness())
	{
		Log::Error("Frame buffer not valid!");
	}

	SharedPointer<IFrameBuffer> PingFrameBuffer = Context->CreateFrameBuffer();

	SharedPointer<ITexture2D> PingColor = GraphicsAPI->CreateTexture2D(Window->GetSize(), ITexture::EMipMaps::False, ITexture::EFormatComponents::RGB, ITexture::EInternalFormatType::Fix8);
	PingColor->SetMinFilter(ITexture::EFilter::Nearest);
	PingColor->SetMagFilter(ITexture::EFilter::Nearest);
	PingColor->SetWrapMode(ITexture::EWrapMode::Clamp);
	PingFrameBuffer->AttachColorTexture(PingColor, 0);
	if (! PingFrameBuffer->CheckCorrectness())
	{
		Log::Error("Frame buffer not valid!");
	}

	SharedPointer<IFrameBuffer> PongFrameBuffer = Context->CreateFrameBuffer();

	SharedPointer<ITexture2D> PongColor = GraphicsAPI->CreateTexture2D(Window->GetSize(), ITexture::EMipMaps::False, ITexture::EFormatComponents::RGB, ITexture::EInternalFormatType::Fix8);
	PongColor->SetMinFilter(ITexture::EFilter::Nearest);
	PongColor->SetMagFilter(ITexture::EFilter::Nearest);
	PongColor->SetWrapMode(ITexture::EWrapMode::Clamp);
	PongFrameBuffer->AttachColorTexture(PongColor, 0);
	if (! PongFrameBuffer->CheckCorrectness())
	{
		Log::Error("Frame buffer not valid!");
	}

	/////////////////
	// Load Assets //
	/////////////////

	CSimpleMesh * SphereMesh = CGeometryCreator::CreateSphere();
	CSimpleMesh * PlaneMesh = CGeometryCreator::CreatePlane(vec2f(100.f));

	SharedPointer<IShader> GeometryShader = AssetManager->LoadShader("Geometry");
	SharedPointer<IShader> SSAOShader = AssetManager->LoadShader("SSAO");
	SharedPointer<IShader> BlurHShader = AssetManager->LoadShader("BlurH");
	SharedPointer<IShader> BlurVShader = AssetManager->LoadShader("BlurV");


	std::uniform_real_distribution<float> randomFloats(0.0, 1.0); // generates random floats between 0.0 and 1.0
	std::default_random_engine generator;

	// Sample kernel
	int const numSamples = 64;
	std::vector<vec3f> ssaoKernel;
	for (uint i = 0; i < numSamples; ++i)
	{
		float const VerticalBias = 0.1f;

		bool random = true;

		int const sampleRegionWidth = (int) sqrt((float) numSamples);

		float const a = random ? randomFloats(generator) : (float) (i / sampleRegionWidth) / (float) (sampleRegionWidth - 1);
		float const b = random ? randomFloats(generator) : (float) (i % sampleRegionWidth) / (float) (sampleRegionWidth - 1);
		float const c = random ? randomFloats(generator) : (float) (i % 3) / 2.f;

		vec3f sample = vec3f(a * 2 - 1, b * 2 - 1, c * (1.f - VerticalBias) + VerticalBias);
		sample.Normalize();
		sample *= randomFloats(generator);
		float scale = float(i) / numSamples;

		// Scale samples s.t. they're more aligned to center of kernel
		scale = lerp(0.1f, 1.0f, scale * scale);
		sample *= scale;
		ssaoKernel.push_back(sample);
	}

	// Noise texture
	std::vector<float> NoiseData;
	uint const NoiseTexSize = 4;
	for (uint i = 0; i < NoiseTexSize * NoiseTexSize; i++)
	{
		NoiseData.push_back(randomFloats(generator) * 2 - 1);
		NoiseData.push_back(randomFloats(generator) * 2 - 1);
		NoiseData.push_back(0.0);
	}
	SharedPointer<ITexture2D> SSAONoise = GraphicsAPI->CreateTexture2D(vec2i(NoiseTexSize), ITexture::EMipMaps::False, ITexture::EFormatComponents::RGB, ITexture::EInternalFormatType::Float16);
	SSAONoise->Upload(NoiseData.data(), vec2i(NoiseTexSize), ITexture::EFormatComponents::RGB, EScalarType::Float);
	SSAONoise->SetMinFilter(ITexture::EFilter::Nearest);
	SSAONoise->SetMagFilter(ITexture::EFilter::Nearest);


	////////////////////
	// ionScene Setup //
	////////////////////

	CRenderPass * RenderPass = new CRenderPass(Context);
	RenderPass->SetRenderTarget(SceneFrameBuffer);
	SceneManager->AddRenderPass(RenderPass);

	CRenderPass * SSAOPass = new CRenderPass(Context);
	SSAOPass->SetRenderTarget(PingFrameBuffer);
	SceneManager->AddRenderPass(SSAOPass);

	int const BlurPasses = 2;

	CRenderPass * BlurHPasses[BlurPasses] =  { nullptr };
	CRenderPass * BlurVPasses[BlurPasses] =  { nullptr };

	for (int i = 0; i < BlurPasses; ++ i)
	{
		BlurHPasses[i] = new CRenderPass(Context);
		BlurHPasses[i]->SetRenderTarget(PongFrameBuffer);
		SceneManager->AddRenderPass(BlurHPasses[i]);

		BlurVPasses[i] = new CRenderPass(Context);
		BlurVPasses[i]->SetRenderTarget(i + 1 == BlurPasses ? BackBuffer : PingFrameBuffer);
		SceneManager->AddRenderPass(BlurVPasses[i]);
	}
	

	CPerspectiveCamera * Camera = new CPerspectiveCamera(Window->GetAspectRatio());
	Camera->SetPosition(vec3f(-1.7f, 2.8f, 3.4f));
	Camera->SetFocalLength(0.4f);
	Camera->SetNearPlane(0.1f);
	Camera->SetFarPlane(50.f);
	RenderPass->SetActiveCamera(Camera);
	SSAOPass->SetActiveCamera(Camera);

	CCameraController * Controller = new CCameraController(Camera);
	Controller->SetTheta(-0.08f);
	Controller->SetPhi(-0.26f);
	Window->AddListener(Controller);
	TimeManager->MakeUpdateTick(0.02)->AddListener(Controller);


	/////////////////
	// Add Objects //
	/////////////////

	CSimpleMeshSceneObject * Sphere1 = new CSimpleMeshSceneObject();
	Sphere1->SetMesh(SphereMesh);
	Sphere1->SetShader(GeometryShader);
	Sphere1->SetPosition(vec3f(0, 0, 0));
	Sphere1->SetScale(2.f);
	Sphere1->GetMaterial().Diffuse *= color3f(1.0, 0.8f, 0.8f);
	RenderPass->AddSceneObject(Sphere1);

	CSimpleMeshSceneObject * Sphere2 = new CSimpleMeshSceneObject();
	Sphere2->SetMesh(SphereMesh);
	Sphere2->SetShader(GeometryShader);
	Sphere2->SetPosition(vec3f(4, 0, 0));
	Sphere2->SetScale(3.f);
	Sphere2->GetMaterial().Diffuse *= color3f(0.8f, 1, 0.8f);
	RenderPass->AddSceneObject(Sphere2);

	CSimpleMeshSceneObject * Sphere3 = new CSimpleMeshSceneObject();
	Sphere3->SetMesh(SphereMesh);
	Sphere3->SetShader(GeometryShader);
	Sphere3->SetPosition(vec3f(12, 0, 0));
	Sphere3->SetScale(4.f);
	Sphere3->GetMaterial().Diffuse *= color3f(0.8f, 0.9f, 1);
	RenderPass->AddSceneObject(Sphere3);

	CSimpleMeshSceneObject * Sphere4 = new CSimpleMeshSceneObject();
	Sphere4->SetMesh(SphereMesh);
	Sphere4->SetShader(GeometryShader);
	Sphere4->SetPosition(vec3f(3, 0, 6));
	Sphere4->GetMaterial().Diffuse *= color3f(0.9f, 1, 1);
	RenderPass->AddSceneObject(Sphere4);

	CSimpleMeshSceneObject * PlaneObject = new CSimpleMeshSceneObject();
	PlaneObject->SetMesh(PlaneMesh);
	PlaneObject->SetShader(GeometryShader);
	RenderPass->AddSceneObject(PlaneObject);

	CSimpleMesh * DragonMesh = AssetManager->LoadMeshMerged("dragon10k.obj");
	CSimpleMeshSceneObject * DragonObject = new CSimpleMeshSceneObject();
	DragonObject->SetMesh(DragonMesh);
	DragonObject->SetShader(GeometryShader);
	DragonObject->SetPosition(vec3f(6.f, 1.1f, 4.f));
	DragonObject->SetScale(4.f);
	RenderPass->AddSceneObject(DragonObject);

	CSimpleMesh * StairMesh = AssetManager->LoadMeshMerged("SM_StairCase_02.obj");
	CSimpleMeshSceneObject * StairObject = new CSimpleMeshSceneObject();
	StairObject->SetMesh(StairMesh);
	StairObject->SetShader(GeometryShader);
	StairObject->SetPosition(vec3f(12.f, 0.f, 5.f));
	StairObject->SetScale(1.f);
	StairObject->SetRotation(vec3f(0.f, DegToRad(90.f), 0.f));
	RenderPass->AddSceneObject(StairObject);

	float SSAORadius = 1.0f;

	CSimpleMeshSceneObject * PostProcessObject = new CSimpleMeshSceneObject();
	PostProcessObject->SetMesh(CGeometryCreator::CreateScreenTriangle());
	PostProcessObject->SetShader(SSAOShader);
	PostProcessObject->SetTexture("tSceneNormals", SceneNormal);
	PostProcessObject->SetTexture("tSceneDepth", SceneDepth);
	PostProcessObject->SetTexture("texNoise", SSAONoise);
	PostProcessObject->SetUniform("samples[0]", CUniform<vector<vec3f>>(ssaoKernel));
	PostProcessObject->SetUniform("radius", std::make_shared<CUniformReference<float>>(&SSAORadius));
	SSAOPass->AddSceneObject(PostProcessObject);

	CUniform<bool> uDoBlur = true;
	CUniform<bool> uUnconstrained = false;
	CUniform<float> uNormalThreshold = 0.5f;

	for (int i = 0; i < BlurPasses; ++ i)
	{
		CSimpleMeshSceneObject * BlurHObject = new CSimpleMeshSceneObject();
		BlurHObject->SetMesh(CGeometryCreator::CreateScreenTriangle());
		BlurHObject->SetShader(BlurHShader);
		BlurHObject->SetTexture("uTexture", PingColor);
		BlurHObject->SetTexture("tSceneNormals", SceneNormal);
		BlurHObject->SetUniform("uDoBlur", uDoBlur);
		BlurHObject->SetUniform("uUnconstrained", uUnconstrained);
		BlurHObject->SetUniform("uNormalThreshold", uNormalThreshold);
		BlurHPasses[i]->AddSceneObject(BlurHObject);

		CSimpleMeshSceneObject * BlurVObject = new CSimpleMeshSceneObject();
		BlurVObject->SetMesh(CGeometryCreator::CreateScreenTriangle());
		BlurVObject->SetShader(BlurVShader);
		BlurVObject->SetTexture("uTexture", PongColor);
		BlurVObject->SetTexture("tSceneNormals", SceneNormal);
		BlurVObject->SetUniform("uDoBlur", uDoBlur);
		BlurVObject->SetUniform("uUnconstrained", uUnconstrained);
		BlurVObject->SetUniform("uNormalThreshold", uNormalThreshold);
		BlurVPasses[i]->AddSceneObject(BlurVObject);
	}

	CPointLight * Light1 = new CPointLight();
	Light1->SetPosition(vec3f(0, 6, 0));
	RenderPass->AddLight(Light1);


	///////////////
	// Main Loop //
	///////////////

	TimeManager->Init(WindowManager);
	while (WindowManager->Run())
	{
		TimeManager->Update();

		GUIManager->NewFrame();
		ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiSetCond_Once);
		if (ImGui::Begin("Settings"))
		{
			ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
			ImGui::Text("Camera position: %.3f %.3f %.3f", Camera->GetPosition().X, Camera->GetPosition().Y, Camera->GetPosition().Z);
			ImGui::Text("Camera rotation: %.3f %.3f", Controller->GetTheta(), Controller->GetPhi());

			ImGui::Separator();

			ImGui::SliderFloat("SSAO Radius", &SSAORadius, 0.1f, 20.f);
			ImGui::Checkbox("Do Blur", &uDoBlur.Get());
			ImGui::SameLine();
			ImGui::Checkbox("Unconstrained", &uUnconstrained.Get());
			ImGui::SliderFloat("Normal Threshold", &uNormalThreshold.Get(), -1.f, 1.f);

			ImGui::End();
		}

		SceneFrameBuffer->ClearColorAndDepth();
		BackBuffer->ClearColorAndDepth();
		SceneManager->DrawAll();
		GUIManager->Draw();
		Window->SwapBuffers();
	}

	return 0;
}
int main()
{
	////////////////////
	// ionEngine Init //
	////////////////////

	Log::AddDefaultOutputs();

	SingletonPointer<CGraphicsAPI> GraphicsAPI;
	SingletonPointer<CWindowManager> WindowManager;
	SingletonPointer<CTimeManager> TimeManager;
	SingletonPointer<CSceneManager> SceneManager;
	SingletonPointer<CAssetManager> AssetManager;
	SingletonPointer<CGUIManager> GUIManager;

	GraphicsAPI->Init(new COpenGLImplementation());
	WindowManager->Init(GraphicsAPI);
	TimeManager->Init(WindowManager);
	SceneManager->Init(GraphicsAPI);
	AssetManager->Init(GraphicsAPI);

	CWindow * Window = WindowManager->CreateWindow(vec2i(1920, 1080), "Shadow Maps", EWindowType::Windowed);

	GUIManager->Init(Window);
	Window->AddListener(GUIManager);

	AssetManager->AddAssetPath("Assets/");
	AssetManager->SetShaderPath("Shaders/");
	AssetManager->SetTexturePath("Images/");

	SharedPointer<IGraphicsContext> Context = GraphicsAPI->GetWindowContext(Window);
	SharedPointer<IRenderTarget> BackBuffer = Context->GetBackBuffer();
	BackBuffer->SetClearColor(color3f(0.3f));

	SharedPointer<IFrameBuffer> ShadowBuffer = Context->CreateFrameBuffer();

	SharedPointer<ITexture2D> ShadowTexture = GraphicsAPI->CreateTexture2D(vec2i(4096), ITexture::EMipMaps::False, ITexture::EFormatComponents::RGBA, ITexture::EInternalFormatType::Fix8);
	SharedPointer<ITexture2D> ShadowDepth = GraphicsAPI->CreateTexture2D(vec2i(4096), ITexture::EMipMaps::False, ITexture::EFormatComponents::R, ITexture::EInternalFormatType::Depth);
	ShadowBuffer->AttachColorTexture(ShadowTexture, 0);
	ShadowBuffer->AttachDepthTexture(ShadowDepth);
	if (! ShadowBuffer->CheckCorrectness())
	{
		Log::Error("Frame buffer not valid!");
	}


	/////////////////
	// Load Assets //
	/////////////////

	CSimpleMesh * SphereMesh = CGeometryCreator::CreateSphere();
	CSimpleMesh * PlaneMesh = CGeometryCreator::CreatePlane(vec2f(100.f));
	CSimpleMesh * CubeMesh = CGeometryCreator::CreateCube();

	SharedPointer<IShader> DiffuseShader = AssetManager->LoadShader("Diffuse");
	SharedPointer<IShader> ColorShader = AssetManager->LoadShader("Color");
	SharedPointer<IShader> QuadCopyShader = AssetManager->LoadShader("QuadCopy");


	////////////////////
	// ionScene Setup //
	////////////////////

	CRenderPass * ShadowPass = new CRenderPass(Context);
	ShadowPass->SetRenderTarget(ShadowBuffer);
	SceneManager->AddRenderPass(ShadowPass);

	CRenderPass * ColorPass = new CRenderPass(Context);
	ColorPass->SetRenderTarget(BackBuffer);
	SceneManager->AddRenderPass(ColorPass);

	CRenderPass * PostProcess = new CRenderPass(Context);
	PostProcess->SetRenderTarget(BackBuffer);
	SceneManager->AddRenderPass(PostProcess);

	CPerspectiveCamera * Camera = new CPerspectiveCamera(Window->GetAspectRatio());
	Camera->SetPosition(vec3f(15.25f, 7.3f, -11.85f));
	Camera->SetFocalLength(0.4f);
	Camera->SetFarPlane(1000.f);
	ColorPass->SetActiveCamera(Camera);

	CCameraController * Controller = new CCameraController(Camera);
	Controller->SetTheta(2.347f);
	Controller->SetPhi(-0.326f);
	Window->AddListener(Controller);
	TimeManager->MakeUpdateTick(0.02)->AddListener(Controller);

	COrthographicCamera * LightCamera = new COrthographicCamera(-1, 1, -1, 1);
	ShadowPass->SetActiveCamera(LightCamera);


	/////////////////
	// Add Objects //
	/////////////////

	CSimpleMeshSceneObject * Sphere1 = new CSimpleMeshSceneObject();
	Sphere1->SetMesh(SphereMesh);
	Sphere1->SetShader(DiffuseShader);
	Sphere1->SetPosition(vec3f(0, 1, 0));
	Sphere1->SetScale(2.f);
	ColorPass->AddSceneObject(Sphere1);
	ShadowPass->AddSceneObject(Sphere1);

	CSimpleMeshSceneObject * Sphere2 = new CSimpleMeshSceneObject();
	Sphere2->SetMesh(SphereMesh);
	Sphere2->SetShader(DiffuseShader);
	Sphere2->SetPosition(vec3f(4, 4, 0));
	Sphere2->SetScale(3.f);
	Sphere2->GetMaterial().Ambient *= Color::Basic::Yellow;
	Sphere2->GetMaterial().Diffuse *= Color::Basic::Yellow;
	ColorPass->AddSceneObject(Sphere2);
	ShadowPass->AddSceneObject(Sphere2);

	CSimpleMeshSceneObject * Sphere3 = new CSimpleMeshSceneObject();
	Sphere3->SetMesh(SphereMesh);
	Sphere3->SetShader(DiffuseShader);
	Sphere3->SetPosition(vec3f(12, 2, 0));
	Sphere3->SetScale(4.f);
	ColorPass->AddSceneObject(Sphere3);
	ShadowPass->AddSceneObject(Sphere3);

	CSimpleMeshSceneObject * Sphere4 = new CSimpleMeshSceneObject();
	Sphere4->SetMesh(SphereMesh);
	Sphere4->SetShader(DiffuseShader);
	Sphere4->SetPosition(vec3f(3, 4, 6));
	Sphere4->GetMaterial().Ambient *= Color::Basic::Red;
	Sphere4->GetMaterial().Diffuse *= Color::Basic::Red;
	ColorPass->AddSceneObject(Sphere4);
	ShadowPass->AddSceneObject(Sphere4);

	CSimpleMeshSceneObject * Cube1 = new CSimpleMeshSceneObject();
	Cube1->SetMesh(CubeMesh);
	Cube1->SetShader(DiffuseShader);
	Cube1->SetPosition(vec3f(-4, 4, 0));
	Cube1->SetScale(3.f);
	Cube1->GetMaterial().Ambient *= Color::Basic::Cyan;
	Cube1->GetMaterial().Diffuse *= Color::Basic::Cyan;
	ColorPass->AddSceneObject(Cube1);
	ShadowPass->AddSceneObject(Cube1);

	CSimpleMeshSceneObject * Cube2 = new CSimpleMeshSceneObject();
	Cube2->SetMesh(CubeMesh);
	Cube2->SetShader(DiffuseShader);
	Cube2->SetPosition(vec3f(-12, 2, 0));
	Cube2->SetScale(4.f);
	Cube2->GetMaterial().Ambient *= Color::Basic::Blue;
	Cube2->GetMaterial().Diffuse *= Color::Basic::Blue;
	ColorPass->AddSceneObject(Cube2);
	ShadowPass->AddSceneObject(Cube2);

	CSimpleMeshSceneObject * Plane = new CSimpleMeshSceneObject();
	Plane->SetMesh(PlaneMesh);
	Plane->SetShader(DiffuseShader);
	Plane->GetMaterial().Ambient *= Color::Basic::Green;
	Plane->GetMaterial().Diffuse *= Color::Basic::Green;
	ColorPass->AddSceneObject(Plane);
	ShadowPass->AddSceneObject(Plane);

	vector<CSimpleMesh *> Meshes = CGeometryCreator::LoadOBJFile("bunny.obj");
	for (auto Mesh : Meshes)
	{
		CSimpleMeshSceneObject * PlaneObject = new CSimpleMeshSceneObject();
		PlaneObject->SetMesh(Mesh);
		PlaneObject->SetShader(DiffuseShader);
		PlaneObject->SetPosition(vec3f(3, -1.f, -6));
		PlaneObject->SetScale(vec3f(2.5f));
		PlaneObject->SetRotation(vec3f(0, 3.14f / 4.f, 0));
		ColorPass->AddSceneObject(PlaneObject);
		ShadowPass->AddSceneObject(PlaneObject);
	}

	CLineSceneObject * Lines = new CLineSceneObject();
	Lines->SetShader(ColorShader);
	ColorPass->AddSceneObject(Lines);

	CSimpleMeshSceneObject * PostProcessObject = new CSimpleMeshSceneObject();
	PostProcessObject->SetMesh(CreateScreenQuad());
	PostProcessObject->SetShader(QuadCopyShader);
	PostProcessObject->SetTexture("uTexture", ShadowDepth);
	PostProcess->AddSceneObject(PostProcessObject);

	CDirectionalLight * Light1 = new CDirectionalLight();
	ColorPass->AddLight(Light1);
	ShadowPass->AddLight(Light1);

	CSimpleMeshSceneObject * LightSphere = new CSimpleMeshSceneObject();
	LightSphere->SetMesh(SphereMesh);
	LightSphere->SetShader(DiffuseShader);
	LightSphere->SetScale(0.4f);
	LightSphere->GetMaterial().Ambient = 1.f / 0.75f;
	LightSphere->GetMaterial().Diffuse = 0;
	ColorPass->AddSceneObject(LightSphere);


	CUniform<bool> uDebugShadows = false;
	CUniform<float> uShadowBias = 0.005f;
	CUniform<glm::mat4> uLightMatrix;

	ColorPass->SetUniform("uLightMatrix", uLightMatrix);
	ColorPass->SetUniform("uDebugShadows", uDebugShadows);
	ColorPass->SetUniform("uShadowBias", uShadowBias);
	ColorPass->SetTexture("uShadowMap", ShadowDepth);

	// Obviously the shadow pass does not need these, but this will suppress warnings
	// An object that supports different shaders for different passes is needed
	ShadowPass->SetUniform("uLightMatrix", uLightMatrix);
	ShadowPass->SetUniform("uDebugShadows", uDebugShadows);
	ShadowPass->SetUniform("uShadowBias", uShadowBias);
	ShadowPass->SetTexture("uShadowMap", ShadowDepth);


	///////////////
	// Main Loop //
	///////////////

	vec3f LightDirection = Normalize( vec3f(2, -12, 2) );
	float LightViewSize = 20.f;
	float LightNear = 1.f;
	float LightFar = 30.f;
	float LightDistance = 15.f;


	TimeManager->Init(WindowManager);
	while (WindowManager->Run())
	{
		TimeManager->Update();

		PostProcessObject->SetVisible(Window->IsKeyDown(EKey::F1));

		GUIManager->NewFrame();
		ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiSetCond_Once);
		ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiSetCond_Once);
		if (ImGui::Begin("Settings"))
		{
			ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
			ImGui::Text("Camera position: %.3f %.3f %.3f", Camera->GetPosition().X, Camera->GetPosition().Y, Camera->GetPosition().Z);
			ImGui::Text("Camera rotation: %.3f %.3f", Controller->GetTheta(), Controller->GetPhi());

			ImGui::Separator();

			ImGui::SliderFloat("Light Camera Size", &LightViewSize, 1.f, 200.f);
			ImGui::SliderFloat("Light Near Plane", &LightNear, 1.f, 300.f);
			ImGui::SliderFloat("Light Far Plane", &LightFar, 1.f, 600.f);

			ImGui::SliderFloat("Light Camera Distance", &LightDistance, 1.f, 200.f);
			ImGui::Text("Light Position: %.3f %.3f %.3f", LightCamera->GetPosition().X, LightCamera->GetPosition().Y, LightCamera->GetPosition().Z);

			ImGui::Separator();

			ImGui::Checkbox("Debug Shadows", &uDebugShadows.Get());
			ImGui::SliderFloat("Shadow Bias", &uShadowBias.Get(), 0.0f, 0.5f, "%.6f", 2.f);
		}
		ImGui::End();

		ImGui::SetNextWindowPos(vec2f(600, 10), ImGuiSetCond_Once);
		ImGui::SetNextWindowSize(vec2f(300), ImGuiSetCond_Once);
		if (ImGui::Begin("Shadow Map"))
		{
			vec2f const AvailableSpace = ImGui::GetContentRegionAvail();
			ImGui::Image(GUIManager->GetTextureID(ShadowDepth), AvailableSpace);
		}
		ImGui::End();

		Light1->SetDirection(LightDirection);
		LightSphere->SetPosition(LightDirection * LightDistance);

		LightCamera->SetLeft(-LightViewSize);
		LightCamera->SetRight(LightViewSize);
		LightCamera->SetBottom(-LightViewSize);
		LightCamera->SetTop(LightViewSize);
		LightCamera->SetPosition(-LightDirection * LightDistance);
		LightCamera->SetLookDirection(LightDirection);
		LightCamera->SetNearPlane(LightNear);
		LightCamera->SetFarPlane(LightFar);
		LightCamera->Update();
		uLightMatrix = LightCamera->GetProjectionMatrix() * LightCamera->GetViewMatrix();

		vec3f const W = - Normalize(LightCamera->GetLookDirecton());
		vec3f const U = Normalize(Cross(LightCamera->GetUpVector(), W));
		vec3f const V = Cross(W, U);
		vec3f const P = LightCamera->GetPosition();

		vec3f Box[8];

		float const BoxSize = LightViewSize;
		Box[0] = P - U * BoxSize - V * BoxSize - W * LightNear;
		Box[1] = P + U * BoxSize - V * BoxSize - W * LightNear;
		Box[2] = P - U * BoxSize + V * BoxSize - W * LightNear;
		Box[3] = P + U * BoxSize + V * BoxSize - W * LightNear;

		Box[4] = P - U * BoxSize - V * BoxSize - W * LightFar;
		Box[5] = P + U * BoxSize - V * BoxSize - W * LightFar;
		Box[6] = P - U * BoxSize + V * BoxSize - W * LightFar;
		Box[7] = P + U * BoxSize + V * BoxSize - W * LightFar;

		Lines->ResetLines();
		Lines->AddLine(vec3f(0, 0, 0), P, Color::Basic::Cyan);
		Lines->AddLine(P, P + U, Color::Basic::Red);
		Lines->AddLine(P, P + V, Color::Basic::Green);
		Lines->AddLine(P, P + W, Color::Basic::Blue);

		Lines->AddLine(Box[0], Box[1], Color::Basic::White);
		Lines->AddLine(Box[0], Box[2], Color::Basic::White);
		Lines->AddLine(Box[1], Box[3], Color::Basic::White);
		Lines->AddLine(Box[2], Box[3], Color::Basic::White);

		Lines->AddLine(Box[4], Box[5], Color::Basic::White);
		Lines->AddLine(Box[4], Box[6], Color::Basic::White);
		Lines->AddLine(Box[5], Box[7], Color::Basic::White);
		Lines->AddLine(Box[6], Box[7], Color::Basic::White);

		Lines->AddLine(Box[0], Box[4], Color::Basic::White);
		Lines->AddLine(Box[1], Box[5], Color::Basic::White);
		Lines->AddLine(Box[2], Box[6], Color::Basic::White);
		Lines->AddLine(Box[3], Box[7], Color::Basic::White);
		//Lines->AddLine(vec3f(0, 0, 0), vec3f(0, 10, 0), Color::Basic::Blue);

		ShadowBuffer->ClearColorAndDepth();
		BackBuffer->ClearColorAndDepth();
		SceneManager->DrawAll();


		GUIManager->Draw();

		Window->SwapBuffers();
	}

	return 0;
}
int main()
{
	////////////////////
	// ionEngine Init //
	////////////////////

	Log::AddDefaultOutputs();

	SingletonPointer<CGraphicsAPI> GraphicsAPI;
	SingletonPointer<CWindowManager> WindowManager;
	SingletonPointer<CTimeManager> TimeManager;
	SingletonPointer<CSceneManager> SceneManager;
	SingletonPointer<CAssetManager> AssetManager;
	SingletonPointer<CGUIManager> GUIManager;

	GraphicsAPI->Init(new COpenGLImplementation());
	WindowManager->Init(GraphicsAPI);
	TimeManager->Init(WindowManager);
	SceneManager->Init(GraphicsAPI);
	AssetManager->Init(GraphicsAPI);

	CWindow * Window = WindowManager->CreateWindow(vec2i(1600, 900), "Shadow Maps", EWindowType::Windowed);

	GUIManager->Init(Window);

	AssetManager->AddAssetPath("Assets/");
	AssetManager->SetShaderPath("Shaders/");
	AssetManager->SetTexturePath("Images/");

	SharedPointer<IGraphicsContext> Context = GraphicsAPI->GetWindowContext(Window);
	SharedPointer<IRenderTarget> BackBuffer = Context->GetBackBuffer();
	BackBuffer->SetClearColor(color3f(0.3f));

	SharedPointer<IFrameBuffer> ShadowBuffer = Context->CreateFrameBuffer();

	SharedPointer<ITexture2D> ShadowTexture = GraphicsAPI->CreateTexture2D(vec2u(4096), ITexture::EMipMaps::False, ITexture::EFormatComponents::RGBA, ITexture::EInternalFormatType::Fix8);
	SharedPointer<ITexture2D> ShadowDepth = GraphicsAPI->CreateTexture2D(vec2u(4096), ITexture::EMipMaps::False, ITexture::EFormatComponents::R, ITexture::EInternalFormatType::Depth);
	ShadowBuffer->AttachColorTexture(ShadowTexture, 0);
	ShadowBuffer->AttachDepthTexture(ShadowDepth);
	if (! ShadowBuffer->CheckCorrectness())
	{
		Log::Error("Frame buffer not valid!");
	}


	/////////////////
	// Load Assets //
	/////////////////

	CSimpleMesh * SphereMesh = CGeometryCreator::CreateSphere();
	CSimpleMesh * PlaneMesh = CGeometryCreator::CreatePlane(vec2f(100.f));
	CSimpleMesh * CubeMesh = CGeometryCreator::CreateCube();

	SharedPointer<IShaderProgram> DiffuseShader = AssetManager->LoadShader("Diffuse");
	SharedPointer<IShaderProgram> QuadCopyShader = AssetManager->LoadShader("QuadCopy");


	////////////////////
	// ionScene Setup //
	////////////////////

	CRenderPass * ShadowPass = new CRenderPass(Context);
	ShadowPass->SetRenderTarget(ShadowBuffer);
	SceneManager->AddRenderPass(ShadowPass);

	CRenderPass * ColorPass = new CRenderPass(Context);
	ColorPass->SetRenderTarget(BackBuffer);
	SceneManager->AddRenderPass(ColorPass);

	CRenderPass * PostProcess = new CRenderPass(Context);
	PostProcess->SetRenderTarget(BackBuffer);
	SceneManager->AddRenderPass(PostProcess);

	CPerspectiveCamera * Camera = new CPerspectiveCamera(Window->GetAspectRatio());
	Camera->SetPosition(vec3f(0, 3, -5));
	Camera->SetFocalLength(0.4f);
	ColorPass->SetActiveCamera(Camera);

	CCameraController * Controller = new CCameraController(Camera);
	Controller->SetTheta(15.f * Constants32::Pi / 48.f);
	Controller->SetPhi(-Constants32::Pi / 16.f);
	Window->AddListener(Controller);
	TimeManager->MakeUpdateTick(0.02)->AddListener(Controller);

	vec3f LightDirection = vec3f(2, -12, 2);
	float LightViewSize = 20.f;
	float LightNear = 50.f;
	float LightFar = 200.f;

	COrthographicCamera * LightCamera = new COrthographicCamera(-LightViewSize, LightViewSize, -LightViewSize, LightViewSize);
	LightCamera->SetPosition(-LightDirection * 10.f);
	LightCamera->SetLookDirection(LightDirection);
	LightCamera->SetNearPlane(LightNear);
	LightCamera->SetFarPlane(LightFar);
	ShadowPass->SetActiveCamera(LightCamera);


	/////////////////
	// Add Objects //
	/////////////////

	CSimpleMeshSceneObject * Sphere1 = new CSimpleMeshSceneObject();
	Sphere1->SetMesh(SphereMesh);
	Sphere1->SetShader(DiffuseShader);
	Sphere1->SetPosition(vec3f(0, 1, 0));
	Sphere1->SetScale(2.f);
	ColorPass->AddSceneObject(Sphere1);
	ShadowPass->AddSceneObject(Sphere1);

	CSimpleMeshSceneObject * Sphere2 = new CSimpleMeshSceneObject();
	Sphere2->SetMesh(SphereMesh);
	Sphere2->SetShader(DiffuseShader);
	Sphere2->SetPosition(vec3f(4, 4, 0));
	Sphere2->SetScale(3.f);
	ColorPass->AddSceneObject(Sphere2);
	ShadowPass->AddSceneObject(Sphere2);

	CSimpleMeshSceneObject * Sphere3 = new CSimpleMeshSceneObject();
	Sphere3->SetMesh(SphereMesh);
	Sphere3->SetShader(DiffuseShader);
	Sphere3->SetPosition(vec3f(12, 2, 0));
	Sphere3->SetScale(4.f);
	ColorPass->AddSceneObject(Sphere3);
	ShadowPass->AddSceneObject(Sphere3);

	CSimpleMeshSceneObject * Sphere4 = new CSimpleMeshSceneObject();
	Sphere4->SetMesh(SphereMesh);
	Sphere4->SetShader(DiffuseShader);
	Sphere4->SetPosition(vec3f(3, 4, 6));
	ColorPass->AddSceneObject(Sphere4);
	ShadowPass->AddSceneObject(Sphere4);

	CSimpleMeshSceneObject * Cube1 = new CSimpleMeshSceneObject();
	Cube1->SetMesh(CubeMesh);
	Cube1->SetShader(DiffuseShader);
	Cube1->SetPosition(vec3f(-4, 4, 0));
	Cube1->SetScale(3.f);
	ColorPass->AddSceneObject(Cube1);
	ShadowPass->AddSceneObject(Cube1);

	CSimpleMeshSceneObject * Cube2 = new CSimpleMeshSceneObject();
	Cube2->SetMesh(CubeMesh);
	Cube2->SetShader(DiffuseShader);
	Cube2->SetPosition(vec3f(-12, 2, 0));
	Cube2->SetScale(4.f);
	ColorPass->AddSceneObject(Cube2);
	ShadowPass->AddSceneObject(Cube2);

	CSimpleMeshSceneObject * Plane = new CSimpleMeshSceneObject();
	Plane->SetMesh(PlaneMesh);
	Plane->SetShader(DiffuseShader);
	Plane->GetMaterial().Ambient *= Colors::Green;
	Plane->GetMaterial().Diffuse *= Colors::Green;
	ColorPass->AddSceneObject(Plane);
	ShadowPass->AddSceneObject(Plane);

	//vector<CSimpleMesh *> Meshes = CGeometryCreator::LoadOBJFile("terrain.obj");
	//for (auto Mesh : Meshes)
	//{
	//	CSimpleMeshSceneObject * PlaneObject = new CSimpleMeshSceneObject();
	//	PlaneObject->SetMesh(Mesh);
	//	PlaneObject->SetShader(DiffuseShader);
	//	ColorPass->AddSceneObject(PlaneObject);
	//	ShadowPass->AddSceneObject(PlaneObject);
	//}

	CSimpleMeshSceneObject * PostProcessObject = new CSimpleMeshSceneObject();
	PostProcessObject->SetMesh(CGeometryCreator::CreateScreenTriangle());
	PostProcessObject->SetShader(QuadCopyShader);
	PostProcessObject->SetTexture("uTexture", ShadowDepth);
	PostProcess->AddSceneObject(PostProcessObject);

	CDirectionalLight * Light1 = new CDirectionalLight();
	Light1->SetDirection(LightDirection);
	ColorPass->AddLight(Light1);
	ShadowPass->AddLight(Light1);

	CUniform<glm::mat4> uLightMatrix;
	ColorPass->SetUniform("uLightMatrix", uLightMatrix);
	ColorPass->SetTexture("uShadowMap", ShadowDepth);


	///////////////
	// Main Loop //
	///////////////

	TimeManager->Init(WindowManager);
	while (WindowManager->Run())
	{
		TimeManager->Update();

		PostProcessObject->SetVisible(Window->IsKeyDown(EKey::F1));

		GUIManager->NewFrame();
		ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiSetCond_Once);
		if (ImGui::Begin("Settings"))
		{
			ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
			ImGui::Text("Camera position: %.3f %.3f %.3f", Camera->GetPosition().X, Camera->GetPosition().Y, Camera->GetPosition().Z);
			ImGui::Text("Camera rotation: %.3f %.3f", Controller->GetTheta(), Controller->GetPhi());

			ImGui::Separator();

			ImGui::SliderFloat("Light Camera Size", &LightViewSize, 1.f, 200.f);
			ImGui::SliderFloat("Light Near Plane", &LightNear, 1.f, 300.f);
			ImGui::SliderFloat("Light Far Plane", &LightFar, 1.f, 600.f);
			ImGui::SliderFloat3("Light Direction", LightDirection.Values, -30.f, 30.f);
			ImGui::Text("Light Position: %.3f %.3f %.3f", LightCamera->GetPosition().X, LightCamera->GetPosition().Y, LightCamera->GetPosition().Z);

			ImGui::End();
		}

		LightCamera->SetLeft(-LightViewSize);
		LightCamera->SetRight(LightViewSize);
		LightCamera->SetBottom(-LightViewSize);
		LightCamera->SetTop(LightViewSize);
		LightCamera->SetPosition(-LightDirection * 10.f);
		LightCamera->SetLookDirection(LightDirection);
		LightCamera->SetNearPlane(LightNear);
		LightCamera->SetFarPlane(LightFar);
		uLightMatrix = LightCamera->GetProjectionMatrix() * LightCamera->GetViewMatrix();

		ShadowBuffer->ClearColorAndDepth();
		BackBuffer->ClearColorAndDepth();
		SceneManager->DrawAll();


		GUIManager->Draw();

		Window->SwapBuffers();
	}

	return 0;
}