#include "glad/gl.h" #include "GLFW/glfw3.h" #include "spdlog/spdlog.h" #include "glm/glm.hpp" #include "glm/ext/matrix_clip_space.hpp" #include "glm/ext/matrix_transform.hpp" #include "entt/entt.hpp" #define STB_IMAGE_IMPLEMENTATION #include "Components.h" #include "FreeCamera.h" #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" #include "imgui_internal.h" #include "InputManager.h" #include "stb_image.h" #include "Model.h" #include "ModelManager.h" #include "Scene.h" #include "ShaderManager.h" #include "ShaderProgram.h" #include "Texture.h" #include "TextureManager.h" #include "fmt/base.h" GLFWwindow* gWindow = nullptr; int gWindowWidth = 1920; int gWindowHeight = 1080; int glVersionMajor = 0; int glVersionMinor = 0; float gAspectRatio = 0.f; float gMouseSensitivity = 0.1f; FreeCamera gCamera {}; Scene gScene{}; bool gUiFirstRender = true; void glfw_error_callback(int error, const char* description); void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods); void glfw_framebuffer_size_callback(GLFWwindow* window, int width, int height); void init_glfw(); void create_main_window(); void load_gl(); void load_imgui(); void init_camera(); void load_default_textures(); void load_shaders(); void load_inputs(); void load_default_models(); void loop(); void draw_ui(); auto xAxis = glm::vec3{1.f, 0.f, 0.f}; auto yAxis = glm::vec3{0.f, 1.f, 0.f}; auto zAxis = glm::vec3{0.f, 0.f, 1.f}; int main() { spdlog::info("b_engine v0.0.3 start"); init_glfw(); create_main_window(); load_gl(); load_imgui(); init_camera(); stbi_set_flip_vertically_on_load(true); load_default_textures(); load_shaders(); load_inputs(); load_default_models(); entt::entity dirLight = gScene.create_game_object(); auto& [direction, ambient, diffuse, specular] = gScene.attach_component(dirLight); direction = {1, -1, 1}; ambient = {0.3f, 0.3f, 0.3f}; diffuse = {0.5f, 0.5f, 0.5f}; specular = {1.0f, 1.0f, 1.0f}; auto& tag = gScene.fetch_component(dirLight); tag.name = "directional light"; loop(); ShaderManager::shaders.clear(); TextureManager::textures.clear(); ModelManager::models.clear(); glfwDestroyWindow(gWindow); glfwTerminate(); return 0; } void glfw_error_callback(int error, const char* description) { spdlog::error("glfw error: {}", description); } void glfw_framebuffer_size_callback(GLFWwindow *window, int width, int height) { glViewport(0, 0, width, height); gAspectRatio = (float)width / (float)height; gCamera.update_aspect_ratio(gAspectRatio); } void init_glfw() { if (!glfwInit()) { spdlog::error("could not initialize glfw"); std::exit(1); } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_SAMPLES, 4); glfwSetErrorCallback(glfw_error_callback); } void create_main_window() { gWindow = glfwCreateWindow(gWindowWidth, gWindowHeight, "b_engine v0.0.3", nullptr, nullptr); if (!gWindow) { spdlog::error("failed to create glfw window"); std::exit(1); } glfwMakeContextCurrent(gWindow); glfwSetKeyCallback(gWindow, InputManager::key_callback); glfwSetCursorPosCallback(gWindow, InputManager::mouse_pos_callback); glfwSetFramebufferSizeCallback(gWindow, glfw_framebuffer_size_callback); #ifndef GLFW_CONTEXT_DEBUG glfwSetInputMode(gWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); #endif } void load_gl() { int version = gladLoadGL(glfwGetProcAddress); glVersionMajor = GLAD_VERSION_MAJOR(version); glVersionMinor = GLAD_VERSION_MINOR(version); spdlog::info("gl version {}.{}", glVersionMajor, glVersionMinor); if (gWindow) { int fbWidth, fbHeight; glfwGetFramebufferSize(gWindow, &fbWidth, &fbHeight); gAspectRatio = (float)fbWidth / (float)fbHeight; } glEnable(GL_DEPTH_TEST); glEnable(GL_MULTISAMPLE); } void load_imgui() { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(gWindow, true); ImGui_ImplOpenGL3_Init("#version 330"); } void init_camera() { gCamera = {{5.f, 5.f, 5.f}, 0, 0, gAspectRatio}; gCamera.rotate_yaw(-135.f); gCamera.rotate_pitch(-35.f); InputManager::add_mouse_listener([](float xoff, float yoff) { gCamera.rotate_yaw(xoff * gMouseSensitivity); gCamera.rotate_pitch(yoff * gMouseSensitivity); }); } void load_default_textures() { spdlog::info("creating default textures"); unsigned char defaultDiffuseData[4] = {static_cast(255), 255, 255, 255}; TextureManager::textures["default_diffuse"] = TextureManager::load_from_data(reinterpret_cast(&defaultDiffuseData), 1, 1, 4); unsigned char defaultSpecularData[4] = {static_cast(64), 64, 64, 255}; TextureManager::textures["default_specular"] = TextureManager::load_from_data(reinterpret_cast(&defaultSpecularData), 1, 1, 4); } void load_shaders() { spdlog::info("compiling shaders"); ShaderManager::shaders["phong_shader"] = ShaderManager::load("./resources/standard.vert", "./resources/standard.frag"); if (ShaderManager::shaders["phong_shader"] == nullptr) { spdlog::error("failed to compile phong shader"); std::exit(1); } } void load_inputs() { InputManager::generate_input_action("move_forward", {{GLFW_KEY_W, KEY_DOWN}}); InputManager::generate_input_action("move_backward", {{GLFW_KEY_S, KEY_DOWN}}); InputManager::generate_input_action("move_right", {{GLFW_KEY_D, KEY_DOWN}}); InputManager::generate_input_action("move_left", {{GLFW_KEY_A, KEY_DOWN}}); InputManager::generate_input_action("move_up", {{GLFW_KEY_SPACE, KEY_DOWN}}); InputManager::generate_input_action("move_down", {{GLFW_KEY_LEFT_SHIFT, KEY_DOWN}}); InputManager::generate_input_action("exit_application", {{GLFW_KEY_ESCAPE, KEY_DOWN}}); } void load_default_models() { spdlog::info("loading default models"); ModelManager::models["cube"] = ModelManager::load_from_file("./resources/cube.obj"); ModelManager::models["vette"] = ModelManager::load_from_file("./resources/c4/C4Fixed.obj"); ModelManager::models["male"] = ModelManager::load_from_file("./resources/male.obj"); } void loop() { while (!glfwWindowShouldClose(gWindow)) { glfwPollEvents(); // check inputs /*if (InputManager::check_action_performed("exit_application")) { glfwSetWindowShouldClose(gWindow, true); continue; }*/ if (InputManager::check_action_performed("move_forward")) { gCamera.move(FORWARD, .05); } if (InputManager::check_action_performed("move_backward")) { gCamera.move(BACKWARD, .05); } if (InputManager::check_action_performed("move_left")) { gCamera.move(LEFT, .05); } if (InputManager::check_action_performed("move_right")) { gCamera.move(RIGHT, .05); } if (InputManager::check_action_performed("move_up")) { gCamera.move(UP, .05); } if (InputManager::check_action_performed("move_down")) { gCamera.move(DOWN, .05); } draw_ui(); // gl frame prep glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); const auto shader = ShaderManager::shaders["phong_shader"]; shader->bind(); shader->setMat4("projection", gCamera.projection()); shader->setMat4("view", gCamera.view()); shader->setVec3("viewPosition", gCamera.position()); gScene.draw_scene(shader.get()); glBindVertexArray(0); ShaderProgram::unbind(); // gl end frame stuff glfwSwapBuffers(gWindow); } } void draw_ui() { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); { ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; ImGui::Begin("b_engine dockspace", nullptr, window_flags); ImGui::PopStyleVar(2); ImGuiID dockspace_id = ImGui::GetID("bengine_dockspace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None); if (gUiFirstRender) { gUiFirstRender = false; ImGui::DockBuilderRemoveNode(dockspace_id); // Clear any previous layout ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace); ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size); ImGuiID dock_main_id = dockspace_id; // The center remains for the game view const ImGuiID dock_id_left = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.2f, nullptr, &dock_main_id); const ImGuiID dock_id_right = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.25f, nullptr, &dock_main_id); // Assign your windows to these specific dock IDs ImGui::DockBuilderDockWindow("Hierarchy", dock_id_left); ImGui::DockBuilderDockWindow("Inspector", dock_id_right); ImGui::DockBuilderDockWindow("Scene", dock_main_id); ImGui::DockBuilderFinish(dockspace_id); } ImGui::Begin("Hierarchy"); ImGui::Text("List of GameObjects..."); ImGui::End(); ImGui::Begin("Inspector"); ImGui::Text("Component Properties..."); ImGui::End(); ImGui::Begin("Scene"); ImGui::Text("This is where your 3D view goes!"); ImGui::End(); if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { if (ImGui::MenuItem("New Scene")) { /* Handle New File logic */ } if (ImGui::MenuItem("Open Scene")) { /* Handle Open logic */ } ImGui::Separator(); // Adds a visual line between sections if (ImGui::MenuItem("Save", "Ctrl+S")) { /* Handle Save logic */ } if (ImGui::MenuItem("Exit", "Alt+F4")) { glfwSetWindowShouldClose(gWindow, true); } ImGui::EndMenu(); } if (ImGui::BeginMenu("Scene")) { if (ImGui::MenuItem("New Scene Object")) { gScene.create_game_object(); // Do other stuff here, open this entity in the entity editor, etc... } ImGui::EndMenu(); } ImGui::EndMainMenuBar(); } ImGui::End(); } ImGui::Render(); }