text: ﻿#define IMGUI_IMPL_OPENGL_LOADER_GLAD
  #define STB_IMAGE_IMPLEMENTATION
  #include<glad/glad.h>
  #include<GLFW/glfw3.h>
  #include<stb_image.h>
  
  #include<glm/glm.hpp>
  #include<glm/gtc/matrix_transform.hpp>
  #include<glm/gtc/type_ptr.hpp>
  
  #include"ImGui/imgui.h"
  #include"ImGui/impl/imgui_impl_glfw.h"
  #include"ImGui/impl/imgui_impl_opengl3.h"
  
  #include<GLShader.h>
  #include<GLCamera.h>
  
  #include <iostream>
  
  void ProcessInput(GLFWwindow* window);
  void FrameBufferSizeCallback(GLFWwindow* window, int width, int height);
  void MouseCallback(GLFWwindow* window, double xPos, double yPos);
  void ScrollCallback(GLFWwindow* window, double xOffset, double yOffset);
  unsigned int LoadTexture(const char* path);
  
  //setting
  const unsigned int SCR_WIDTH = 800;
  const unsigned int SCR_HEIGHT = 600;
  
  //camera
  GLCamera camera(glm::vec3(0.0f, 0.0f, 6.0f));
  float lastX = SCR_WIDTH / 2.0f;
  float lastY = SCR_HEIGHT / 2.0f;
  bool firstMouse = true;
  
  //timing
  float deltaTime = 0.0f;		// time between current frame and last frame
  float lastTime = 0.0f;
  
  //lighting
  glm::vec3 lightPos = glm::vec3(0.7f, -0.7f, 1.4f);
  
  int main()
  {
  	glfwInit();
  	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  #ifdef _APPLE_
  	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  #endif // _APPLE_
  
  	//glfw:create window
  	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LightTest", NULL, NULL);
  	if (window == NULL)
  	{
  		std::cout << "Failed to create GLFW window" << std::endl;
  		glfwTerminate();
  		return -1;
  	}
  	glfwMakeContextCurrent(window);
  	glfwSetFramebufferSizeCallback(window, FrameBufferSizeCallback);
  	glfwSetCursorPosCallback(window, MouseCallback);
  	glfwSetScrollCallback(window, ScrollCallback);
  	//tell GLFW to capture our mouse
  	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  
  	//glad
  	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
  	{
  		std::cout << "Failed to initialize GLAD" << std::endl;
  		return -1;
  	}
  
  	//configure global opengl state
  	glEnable(GL_DEPTH_TEST);
  
  	//build shader
  	GLShader cubeShader("shader/cubeVertShader.vert", "shader/cubeFragShader.frag");
  	GLShader lightShader("shader/lightVertShader.vert", "shader/lightFragShader.frag");
  
  	//--------------------------------
  	//Set ImGui
  	//set ImGui context
  	IMGUI_CHECKVERSION();
  	ImGui::CreateContext();
  	ImGuiIO& io = ImGui::GetIO(); (void)io;
  	//setup ImGui style
  	ImGui::StyleColorsLight();	//LightStyle
  	//setup font
  	io.Fonts->AddFontFromFileTTF("ImGui/fonts/msyh.ttc", 18.0f, NULL, io.Fonts->GetGlyphRangesChineseFull());
  	//setup platform/renderer bindings
  	const char* glsl_version = "#version 330";	//OpenGL3.3
  	ImGui_ImplGlfw_InitForOpenGL(window, true);
  	ImGui_ImplOpenGL3_Init(glsl_version);
  	//--------------------------------
  
  	//set vertex data and configure vertex attributes
  	float vertices[] = {
  		// positions          // normals           // texture coords
          -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
           0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,
           0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
           0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
          -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,
          -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
  
          -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,
           0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,
           0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
           0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
          -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,
          -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,
  
          -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
          -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
          -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
          -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
          -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
          -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
  
           0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
           0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
           0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
           0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
           0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
           0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
  
          -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,
           0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,
           0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
           0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
          -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,
          -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,
  
          -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,
           0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,
           0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
           0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
          -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,
          -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
  	};
  
  	//configure the cube's VAO and VBO
  	unsigned int cubeVAO, VBO;
  	glGenVertexArrays(1, &cubeVAO);
  	glGenBuffers(1, &VBO);
  
  	glBindVertexArray(cubeVAO);
  
  	glBindBuffer(GL_ARRAY_BUFFER, VBO);
  	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  
  	//Position Attribute
  	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
  	glEnableVertexAttribArray(0);
  	//Normal Attribute
  	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
  	glEnableVertexAttribArray(1);
  	//Texture coords Attribute
  	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
  	glEnableVertexAttribArray(2);
  
  	//configure light's VAO
  	unsigned int lightVAO;
  	glGenVertexArrays(1, &lightVAO);
  
  	glBindVertexArray(lightVAO);
  
  	glBindBuffer(GL_ARRAY_BUFFER, VBO);
  
  	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
  	glEnableVertexAttribArray(0);
  
  	//load textures
  	unsigned int diffuseMap = LoadTexture("map/diffuseMap.png");
  	unsigned int specularMap = LoadTexture("map/specularMap.png");
  	//unsigned int specularMap = LoadTexture("map/specularMap_color.png");
  
  	cubeShader.use();
  	cubeShader.SetInt("material.difffuse", 0);
  	cubeShader.SetInt("material.specular", 1);
  
  	//render loop
  	//-----------
  	while (!glfwWindowShouldClose(window))
  	{
  		// per-frame time logic
  		// --------------------
  		float currentFrame = glfwGetTime();
  		deltaTime = currentFrame - lastTime;
  		lastTime = currentFrame;
  
  		// input
  		// -----
  		ProcessInput(window);
  
  		//start the ImGui frame
  		//---------------------
  		ImGui_ImplOpenGL3_NewFrame();
  		ImGui_ImplGlfw_NewFrame();
  		ImGui::NewFrame();
  
  		//SnowyArkMini
  		{
  			const float DISTANCE = 0.0f;
  			static int corner = 0;
  			ImGuiIO& io = ImGui::GetIO();
  			ImGuiWindowFlags SnowyArkMiniWindowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
  			if (corner != -1)
  			{
  				SnowyArkMiniWindowFlags |= ImGuiWindowFlags_NoMove;
  				ImVec2 SnowyArkMiniPos = ImVec2((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE);
  				ImVec2 SnowyArkMiniPosPivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f);
  				ImGui::SetNextWindowPos(SnowyArkMiniPos, ImGuiCond_Always, SnowyArkMiniPosPivot);
  			}
  			ImGui::SetNextWindowBgAlpha(0.35f);// Transparent background
  			ImGui::SetNextWindowSize(ImVec2(215, 100), ImGuiCond_FirstUseEver);
  			if (ImGui::Begin("SnowyArkMini", NULL, SnowyArkMiniWindowFlags))
  			{
  				ImGui::Text("SnowyArk Mini");
  				ImGui::Separator();
  				if (ImGui::IsMousePosValid())
  				{
  					ImGui::Text("Mouse Position:(%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
  				}
  				else
  				{
  					ImGui::Text("Mouse Position:<invalid>");
  				}
  				
  				ImGui::Text("Camera Position:(%.1f,%.1f,%.1f)", camera.Position.x, camera.Position.y, camera.Position.z);
  				ImGui::Text("FPS:%.1f", ImGui::GetIO().Framerate);
  			}
  			ImGui::End();
  		}
  
  		// render
  		// ------
  		ImGui::Render();
  		glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
  		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
  
  		//lightPos
  		/*lightPos.x = 1.0f + sin(glfwGetTime()) * 2.0f;
  		lightPos.y = sin(glfwGetTime() / 2.0f) * 1.0f;*/
  
  		// be sure to activate shader when setting uniforms/drawing objects
  		cubeShader.use();
  		cubeShader.SetVec3("light.position", lightPos);
  		cubeShader.SetVec3("viewPos", camera.Position);
  
  		//light properties
  		glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f);
  		glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);		// decrease the influence
  		glm::vec3 ambientColor = diffuseColor * glm::vec3(0.4f);	// low influence
  		cubeShader.SetVec3("light.ambient", ambientColor);
  		cubeShader.SetVec3("light.diffuse", diffuseColor);
  		cubeShader.SetVec3("light.specular", lightColor);
  
  		//material properties
  		cubeShader.SetFloat("material.shininess", 64.0f);
  
  		// view/projection transformations
  		glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
  		glm::mat4 view = camera.GetViewMatrix();
  		cubeShader.SetMat4("projection", projection);
  		cubeShader.SetMat4("view", view);
  
  		//world transformation
  		glm::mat4 model = glm::mat4(1.0f);
  		cubeShader.SetMat4("model", model);
  
  		//bind diffuse map
  		glActiveTexture(GL_TEXTURE0);
  		glBindTexture(GL_TEXTURE_2D, diffuseMap);
  		//bind specular map
  		glActiveTexture(GL_TEXTURE1);
  		glBindTexture(GL_TEXTURE_2D, specularMap);
  
  		//render the cube
  		glBindVertexArray(cubeVAO);
  		glDrawArrays(GL_TRIANGLES, 0, 36);
  
  		//draw the lamp object
  		lightShader.use();
  		lightShader.SetVec3("lightColor", lightColor);
  		lightShader.SetMat4("projection", projection);
  		lightShader.SetMat4("view", view);
  		model = glm::mat4(1.0f);
  		model = glm::translate(model, lightPos);
  		model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube
  		lightShader.SetMat4("model", model);
  
  		glBindVertexArray(lightVAO);
  		glDrawArrays(GL_TRIANGLES, 0, 36);
  
  		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
  		// -------------------------------------------------------------------------------
  		glfwSwapBuffers(window);
  		glfwPollEvents();
  	}
  	ImGui_ImplOpenGL3_Shutdown();
  	ImGui_ImplGlfw_Shutdown();
  	ImGui::DestroyContext();
  
  	glDeleteVertexArrays(1, &cubeVAO);
  	glDeleteVertexArrays(1, &lightVAO);
  	glDeleteBuffers(1, &VBO);
  
  	// glfw: terminate, clearing all previously allocated GLFW resources.
  	// ------------------------------------------------------------------
  	glfwTerminate();
  	return 0;
  }
  
  void ProcessInput(GLFWwindow* window)
  {
  	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
  	{
  		glfwSetWindowShouldClose(window, true);
  	}
  	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
  	{
  		camera.ProcessKeyboard(FORWARD, deltaTime);
  	}
  	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
  	{
  		camera.ProcessKeyboard(BACKWARD, deltaTime);
  	}
  	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
  	{
  		camera.ProcessKeyboard(LEFT, deltaTime);
  	}
  	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
  	{
  		camera.ProcessKeyboard(RIGHT, deltaTime);
  	}
  	if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
  	{
  		camera.ProcessKeyboard(RISE, deltaTime);
  	}
  	if (glfwGetKey(window, GLFW_KEY_CAPS_LOCK) == GLFW_PRESS)
  	{
  		camera.ProcessKeyboard(FALL, deltaTime);
  	}
  }
  void FrameBufferSizeCallback(GLFWwindow* window, int width, int height)
  {
  	glViewport(0, 0, width, height);
  }
  
  void MouseCallback(GLFWwindow* window, double xPos, double yPos)
  {
  	if (firstMouse)
  	{
  		lastX = xPos;
  		lastY = yPos;
  		firstMouse = false;
  	}
  	float xoffset = xPos - lastX;
  	float yoffset = lastY - yPos;
  	lastX = xPos;
  	lastY = yPos;
  
  	camera.ProcessMouseMovement(xoffset, yoffset);
  }
  void ScrollCallback(GLFWwindow* window, double xOffset, double yOffset)
  {
  	camera.ProcessMouseScroll(yOffset);
  }
  
  unsigned int LoadTexture(const char* path)
  {
  	unsigned int textureID;
  	glGenTextures(1, &textureID);
  
  	int width, height, nrComponents;
  	//stbi_set_flip_vertically_on_load(true);
  	unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);
  	if (data)
  	{
  		GLenum format;
  		if (nrComponents == 1)
  			format = GL_RED;
  		else if (nrComponents == 3)
  			format = GL_RGB;
  		else if (nrComponents == 4)
  			format = GL_RGBA;
  
  		glBindTexture(GL_TEXTURE_2D, textureID);
  		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
  		glGenerateMipmap(GL_TEXTURE_2D);
  
  		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  
  		stbi_image_free(data);
  	}
  	else 
  	{
  		std::cout << "Texture failed to load at path: " << path << std::endl;
  		stbi_image_free(data);
  	}
  	return textureID;
  }
  