#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "XCEngine/Debug/Logger.h" #include "XCEngine/Debug/ConsoleLogSink.h" #include "XCEngine/Debug/FileLogSink.h" #include "XCEngine/RHI/OpenGL/OpenGLShader.h" using namespace XCEngine::Debug; using namespace XCEngine::RHI; #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "gdi32.lib") const unsigned int SCR_WIDTH = 1280; const unsigned int SCR_HEIGHT = 720; void Log(const char* format, ...) { char buffer[1024]; va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); Logger::Get().Debug(LogCategory::Rendering, buffer); } struct Vertex { glm::vec3 Position; glm::vec3 Normal; glm::vec2 TexCoords; }; struct Texture { unsigned int id; std::string type; std::string path; }; class Mesh { public: std::vector vertices; std::vector indices; std::vector textures; unsigned int VAO; Mesh(std::vector _vertices, std::vector _indices, std::vector _textures) { vertices = _vertices; indices = _indices; textures = _textures; setupMesh(); } void Draw(OpenGLShader* shader) { unsigned int diffuseNr = 1; unsigned int specularNr = 1; for (unsigned int i = 0; i < textures.size(); i++) { glActiveTexture(GL_TEXTURE0 + i); std::string number; std::string name = textures[i].type; if (name == "texture_diffuse") number = std::to_string(diffuseNr++); else if (name == "texture_specular") number = std::to_string(specularNr++); glUniform1i(glGetUniformLocation(shader->GetID(), ("material." + name + number).c_str()), i); glBindTexture(GL_TEXTURE_2D, textures[i].id); } glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, static_cast(indices.size()), GL_UNSIGNED_INT, 0); glActiveTexture(GL_TEXTURE0); } private: unsigned int VBO, EBO; void setupMesh() { glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords)); glBindVertexArray(0); } }; unsigned int TextureFromFile(const char* path, const std::string& directory) { std::string filename = directory + '/' + std::string(path); unsigned int textureID; glGenTextures(1, &textureID); int width, height, nrComponents; stbi_set_flip_vertically_on_load(true); unsigned char* data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0); if (data) { GLenum format = (nrComponents == 4) ? GL_RGBA : GL_RGB; 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); Log("Texture loaded: %s (%dx%d)", filename.c_str(), width, height); stbi_image_free(data); } else { Log("Failed to load texture: %s", filename.c_str()); stbi_image_free(data); } return textureID; } class Model { public: std::vector textures_loaded; std::vector meshes; std::string directory; Model(std::string const& path) { loadModel(path); } void Draw(OpenGLShader* shader) { for (auto& mesh : meshes) mesh.Draw(shader); } private: void loadModel(std::string const& path) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs); if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { Log("Failed to load model: %s", path.c_str()); return; } Log("Model loaded: %s", path.c_str()); directory = path.substr(0, path.find_last_of('/')); processNode(scene->mRootNode, scene); } void processNode(aiNode* node, const aiScene* scene) { for (unsigned int i = 0; i < node->mNumMeshes; i++) { aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; meshes.push_back(processMesh(mesh, scene)); } for (unsigned int i = 0; i < node->mNumChildren; i++) processNode(node->mChildren[i], scene); } Mesh processMesh(aiMesh* mesh, const aiScene* scene) { std::vector vertices; std::vector indices; std::vector textures; for (unsigned int i = 0; i < mesh->mNumVertices; i++) { Vertex vertex; vertex.Position = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z); vertex.Normal = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z); if (mesh->mTextureCoords[0]) vertex.TexCoords = glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y); else vertex.TexCoords = glm::vec2(0.0f, 0.0f); vertices.push_back(vertex); } for (unsigned int i = 0; i < mesh->mNumFaces; i++) { aiFace face = mesh->mFaces[i]; for (unsigned int j = 0; j < face.mNumIndices; j++) indices.push_back(face.mIndices[j]); } aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; std::vector diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"); textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); std::vector specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"); textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); return Mesh(vertices, indices, textures); } std::vector loadMaterialTextures(aiMaterial* mat, aiTextureType type, std::string typeName) { std::vector textures; for (unsigned int i = 0; i < mat->GetTextureCount(type); i++) { aiString str; mat->GetTexture(type, i, &str); bool skip = false; for (auto& tex : textures_loaded) { if (tex.path == str.C_Str()) { textures.push_back(tex); skip = true; break; } } if (!skip) { Texture texture; texture.id = TextureFromFile(str.C_Str(), directory); texture.type = typeName; texture.path = str.C_Str(); textures.push_back(texture); textures_loaded.push_back(texture); } } return textures; } }; Model* model = nullptr; OpenGLShader* shader = nullptr; int frameCount = 0; void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } void Initialize() { char exePath[MAX_PATH]; GetModuleFileNameA(NULL, exePath, MAX_PATH); std::string exeDir = std::string(exePath); exeDir = exeDir.substr(0, exeDir.find_last_of("\\/")); SetCurrentDirectoryA(exeDir.c_str()); Log("OpenGL Test Application Started"); model = new Model("res/models/backpack/backpack.obj"); shader = new OpenGLShader(); shader->CompileFromFile("Shaders/vertexshader.glsl", "Shaders/fragmentshader.glsl"); glEnable(GL_DEPTH_TEST); Log("Initialization complete"); } bool SaveScreenshot(const char* filename) { Log("Saving screenshot: %s", filename); int width = SCR_WIDTH; int height = SCR_HEIGHT; std::vector pixels(width * height * 3); glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels.data()); FILE* fp = fopen(filename, "wb"); if (!fp) { Log("Failed to open file for screenshot: %s", filename); return false; } fprintf(fp, "P6\n%d %d\n255\n", width, height); std::vector flippedPixels(width * height * 3); for (int y = 0; y < height; y++) { memcpy(&flippedPixels[y * width * 3], &pixels[(height - 1 - y) * width * 3], width * 3); } fwrite(flippedPixels.data(), 1, width * height * 3, fp); fclose(fp); Log("Screenshot saved successfully"); return true; } void Render() { glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); shader->Use(); glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 modelMat = glm::mat4(1.0f); glm::mat4 view = glm::lookAt(glm::vec3(0, 0, 3), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); shader->SetMat4("view", &view[0][0]); shader->SetMat4("model", &modelMat[0][0]); shader->SetMat4("projection", &projection[0][0]); shader->SetVec3("viewPos", 0.0f, 0.0f, 3.0f); shader->SetFloat("material.shininess", 32.0f); shader->SetVec3("dirLight.direction", 0.0f, -1.0f, 0.0f); shader->SetVec3("dirLight.ambient", 0.3f, 0.3f, 0.3f); shader->SetVec3("dirLight.diffuse", 0.8f, 0.8f, 0.8f); shader->SetVec3("dirLight.specular", 0.5f, 0.5f, 0.5f); model->Draw(shader); } int main() { Logger::Get().AddSink(std::make_unique("OpenGL_engine_log.txt")); Logger::Get().SetMinimumLevel(LogLevel::Debug); AllocConsole(); freopen("CONOUT$", "w", stdout); glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "XCRender", NULL, NULL); if (window == NULL) { Log("Failed to create GLFW window"); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { Log("Failed to initialize GLAD"); return -1; } Log("OpenGL Version: %s", glGetString(GL_VERSION)); Log("Renderer: %s", glGetString(GL_RENDERER)); Initialize(); while (!glfwWindowShouldClose(window)) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); Render(); glfwSwapBuffers(window); glfwPollEvents(); frameCount++; if (frameCount == 30) { Log("Saving screenshot at frame %d", frameCount); SaveScreenshot("screenshot.ppm"); glfwSetWindowShouldClose(window, true); } } Log("Application closed"); glfwTerminate(); Logger::Get().Shutdown(); return 0; }