diff --git a/CMakeLists.txt b/CMakeLists.txt index ec4e2aa..56431db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,5 +39,6 @@ endif() add_subdirectory(third_party) add_subdirectory(engine) +add_subdirectory(editor) add_subdirectory(testbed) -add_subdirectory(examples) \ No newline at end of file +add_subdirectory(examples) diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt new file mode 100644 index 0000000..151e498 --- /dev/null +++ b/editor/CMakeLists.txt @@ -0,0 +1,26 @@ +set(NAME editor) +set(ENGINE_NAME ${PROJECT_NAME}) + +file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp src/*.cpp) + +add_library(${NAME}_deps INTERFACE) + +target_link_libraries(${NAME}_deps INTERFACE + ${ENGINE_NAME} +) + +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) +set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/build") + +add_executable(editor ${SOURCES}) + +target_link_libraries(editor PRIVATE ${NAME}_deps) + +target_include_directories(editor PRIVATE + "${CMAKE_SOURCE_DIR}/engine/include" + "${CMAKE_SOURCE_DIR}/external/imgui" +) + +target_compile_definitions(editor PRIVATE + EDITOR_SHADER_DIR="${CMAKE_CURRENT_SOURCE_DIR}/shaders" +) diff --git a/editor/main.cpp b/editor/main.cpp new file mode 100644 index 0000000..58d939c --- /dev/null +++ b/editor/main.cpp @@ -0,0 +1,25 @@ +#include +#include + +#include "panels/asset_browser_panel.hpp" +#include "panels/inspector_panel.hpp" +#include "panels/scene_hierarchy_panel.hpp" + +class Editor : public sophia::Application { + public: + Editor() : Application{{.width = 750, .height = 1000, .name = "Editor"}} {} + + void onCreate() override { + std::cout << "Editor created" << std::endl; + + debugUI().show(); + + panels().emplace(); + panels().emplace(); + panels().emplace(); + } + + void onUpdate(const sophia::FrameInfo& frameInfo) override {} +}; + +SOPHIA_MAIN(Editor) diff --git a/editor/panels/asset_browser_panel.cpp b/editor/panels/asset_browser_panel.cpp new file mode 100644 index 0000000..5371e4f --- /dev/null +++ b/editor/panels/asset_browser_panel.cpp @@ -0,0 +1,27 @@ +#include "asset_browser_panel.hpp" + +#include + +void AssetBrowserPanel::onUpdate(const sophia::FrameInfo&) { + ImGui::Begin("Asset Browser"); + + ImGui::BeginChild("Folders", ImVec2(150, 0), true); + for (int i = 0; i < static_cast(folders.size()); i++) { + if (ImGui::Selectable(folders[static_cast(i)].name.c_str(), + selectedFolder == i)) { + selectedFolder = i; + } + } + ImGui::EndChild(); + + ImGui::SameLine(); + + ImGui::BeginChild("Files", ImVec2(0, 0), true); + const Folder& folder = folders[static_cast(selectedFolder)]; + for (const std::string& file : folder.files) { + ImGui::Selectable(file.c_str()); + } + ImGui::EndChild(); + + ImGui::End(); +} diff --git a/editor/panels/asset_browser_panel.hpp b/editor/panels/asset_browser_panel.hpp new file mode 100644 index 0000000..fc9577f --- /dev/null +++ b/editor/panels/asset_browser_panel.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include + +class AssetBrowserPanel : public sophia::Panel { + public: + AssetBrowserPanel() : Panel{"Asset Browser"} {} + + void onUpdate(const sophia::FrameInfo& frameInfo) override; + + private: + struct Folder { + std::string name; + std::vector files; + }; + + std::vector folders = { + {"Models", {"player_capsule.fbx", "ground_plane.fbx", "enemy_grunt.fbx"}}, + {"Textures", + {"brick_diffuse.png", "grass_albedo.png", "player_diffuse.png"}}, + {"Materials", {"player_mat.mat", "ground_mat.mat"}}, + {"Scripts", {"PlayerController.lua", "EnemyAI.lua"}}, + {"Scenes", {"MainMenu.scene", "Level_01.scene"}}, + }; + + int selectedFolder = 0; +}; diff --git a/editor/panels/inspector_panel.cpp b/editor/panels/inspector_panel.cpp new file mode 100644 index 0000000..0910b10 --- /dev/null +++ b/editor/panels/inspector_panel.cpp @@ -0,0 +1,36 @@ +#include "inspector_panel.hpp" + +#include + +#include + +void InspectorPanel::onUpdate(const sophia::FrameInfo&) { + ImGui::Begin("Inspector"); + + char nameBuf[128]; + std::snprintf(nameBuf, sizeof(nameBuf), "%s", name.c_str()); + if (ImGui::InputText("Name", nameBuf, sizeof(nameBuf))) { + name = nameBuf; + } + ImGui::Text("Tag: %s", tag.c_str()); + + ImGui::Separator(); + + if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::DragFloat3("Position", &position.x, 0.1f); + ImGui::DragFloat3("Rotation", &rotation.x, 0.1f); + ImGui::DragFloat3("Scale", &scale.x, 0.1f); + } + + if (ImGui::CollapsingHeader("Mesh Renderer", + ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Text("Mesh: %s", meshName.c_str()); + ImGui::Text("Material: %s", materialName.c_str()); + } + + if (ImGui::CollapsingHeader("Script", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Text("%s", scriptName.c_str()); + } + + ImGui::End(); +} diff --git a/editor/panels/inspector_panel.hpp b/editor/panels/inspector_panel.hpp new file mode 100644 index 0000000..a316315 --- /dev/null +++ b/editor/panels/inspector_panel.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +class InspectorPanel : public sophia::Panel { + public: + InspectorPanel() : Panel{"Inspector"} {} + + void onUpdate(const sophia::FrameInfo& frameInfo) override; + + private: + std::string name = "Player"; + std::string tag = "Player"; + + glm::vec3 position{0.0f, 1.0f, 0.0f}; + glm::vec3 rotation{0.0f, 0.0f, 0.0f}; + glm::vec3 scale{1.0f, 1.0f, 1.0f}; + + std::string meshName = "player_capsule.fbx"; + std::string materialName = "player_mat"; + std::string scriptName = "PlayerController.lua"; +}; diff --git a/editor/panels/scene_hierarchy_panel.cpp b/editor/panels/scene_hierarchy_panel.cpp new file mode 100644 index 0000000..14d663c --- /dev/null +++ b/editor/panels/scene_hierarchy_panel.cpp @@ -0,0 +1,34 @@ +#include "scene_hierarchy_panel.hpp" + +#include + +void SceneHierarchyPanel::drawNode(const Node& node) { + if (node.children.empty()) { + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf | + ImGuiTreeNodeFlags_NoTreePushOnOpen | + ImGuiTreeNodeFlags_SpanAvailWidth; + if (node.name == selected) { + flags |= ImGuiTreeNodeFlags_Selected; + } + ImGui::TreeNodeEx(node.name.c_str(), flags); + if (ImGui::IsItemClicked()) { + selected = node.name; + } + return; + } + + if (ImGui::TreeNodeEx(node.name.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth)) { + for (const Node& child : node.children) { + drawNode(child); + } + ImGui::TreePop(); + } +} + +void SceneHierarchyPanel::onUpdate(const sophia::FrameInfo&) { + ImGui::Begin("Scene Hierarchy"); + for (const Node& node : nodes) { + drawNode(node); + } + ImGui::End(); +} diff --git a/editor/panels/scene_hierarchy_panel.hpp b/editor/panels/scene_hierarchy_panel.hpp new file mode 100644 index 0000000..78d70c1 --- /dev/null +++ b/editor/panels/scene_hierarchy_panel.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include + +class SceneHierarchyPanel : public sophia::Panel { + public: + SceneHierarchyPanel() : Panel{"Scene Hierarchy"} {} + + void onUpdate(const sophia::FrameInfo& frameInfo) override; + + private: + struct Node { + std::string name; + std::vector children; + }; + + void drawNode(const Node& node); + + std::vector nodes = { + {"Main Camera", {}}, + {"Directional Light", {}}, + {"Ground", {}}, + {"Player", {}}, + {"Enemies", {{"Enemy_01", {}}, {"Enemy_02", {}}, {"Enemy_03", {}}}}, + {"Props", {{"Crate_01", {}}, {"Crate_02", {}}}}, + }; + + std::string selected; +}; diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index aa601d4..41f862c 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -19,6 +19,7 @@ target_include_directories(${ENGINE_NAME} PUBLIC target_link_libraries(${ENGINE_NAME} PUBLIC vulkan + shaderc glfw glm imgui diff --git a/testbed/CMakeLists.txt b/testbed/CMakeLists.txt index f03f0a6..abf111d 100644 --- a/testbed/CMakeLists.txt +++ b/testbed/CMakeLists.txt @@ -3,15 +3,10 @@ set(ENGINE_NAME ${PROJECT_NAME}) file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp src/*.cpp) -add_library(deps INTERFACE) +add_library(${NAME}_deps INTERFACE) -target_link_libraries(deps INTERFACE +target_link_libraries(${NAME}_deps INTERFACE ${ENGINE_NAME} - vulkan - shaderc - glfw - glm - imgui ) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) @@ -19,7 +14,7 @@ set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CM add_executable(testbed main.cpp) -target_link_libraries(testbed PRIVATE deps) +target_link_libraries(testbed PRIVATE ${NAME}_deps) target_include_directories(testbed PRIVATE "${CMAKE_SOURCE_DIR}/engine/include"