diff --git a/.gitmodules b/.gitmodules index 906cce5..da10699 100644 --- a/.gitmodules +++ b/.gitmodules @@ -32,3 +32,6 @@ [submodule "Prism/vendor/ImViewGuizmo"] path = Prism/vendor/ImViewGuizmo url = https://github.com/Ka1serM/ImViewGuizmo +[submodule "Prism/vendor/efsw"] + path = Prism/vendor/efsw + url = https://github.com/SpartanJ/efsw diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index 1de9fd4..9f3bc68 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -2,7 +2,7 @@ project(PrismEditor) set(CMAKE_BINARY_DIR ${CMAKE_BINARY_DIR}/bin) -file(GLOB ASSETS assets) +file(GLOB ASSETS "assets") file(COPY ${ASSETS} DESTINATION ${CMAKE_BINARY_DIR}) # imgui.ini file diff --git a/Editor/Editor/Editor.cpp b/Editor/Editor/Editor.cpp index 3ce8501..5e0d76d 100644 --- a/Editor/Editor/Editor.cpp +++ b/Editor/Editor/Editor.cpp @@ -23,7 +23,7 @@ public: }; -Prism::Application* Prism::CreateApplication() +Prism::Application* Prism::CreateApplication(const CommandArgs args) { - return new Editor({"hello wrold", 1920, 1080}); + return new Editor({"hello world", 1920, 1080, args}); } diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index 5a7c8ef..6de3011 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -280,6 +280,31 @@ namespace Prism { auto& materials = mesh->GetMaterials(); static uint32_t selectedMaterialIndex = 0; + { + ImGui::BeginChild("MaterialList", ImVec2(0, 150), ImGuiChildFlags_Borders); + + static ImGuiTextFilter filter; + filter.Draw("Filter", ImGui::GetContentRegionAvail().x); + + if (ImGui::BeginListBox("##MaterialsListBox", ImVec2(-FLT_MIN, -FLT_MIN))) + { + for (uint32_t i = 0; i < materials.size(); i++) + { + const auto& materialInstance = materials[i]; + std::string name = materialInstance->GetName(); + + if (!filter.PassFilter(name.c_str())) + continue; + + bool isSelected = (selectedMaterialIndex == i); + if (ImGui::Selectable(name.c_str(), isSelected)) + selectedMaterialIndex = i; + } + ImGui::EndListBox(); + } + ImGui::EndChild(); + } + /* for (uint32_t i = 0; i < materials.size(); i++) { const auto& materialInstance = materials[i]; @@ -294,6 +319,7 @@ namespace Prism ImGui::TreePop(); } + */ ImGui::Separator(); @@ -327,8 +353,7 @@ namespace Prism } if (ImGui::IsItemClicked()) { - std::string filename = Application::Get().OpenFile(""); - if (filename != "") + if (std::string filename = FileSystem::OpenFileSelector("*.png;*.tga;*.jpg;*.jpeg"); !filename.empty()) { albedoMap = Texture2D::Create(filename, true/*m_AlbedoInput.SRGB*/); materialInstance->Set("u_AlbedoTexture", albedoMap); @@ -374,8 +399,7 @@ namespace Prism } if (ImGui::IsItemClicked()) { - std::string filename = Application::Get().OpenFile(""); - if (filename != "") + if (std::string filename = FileSystem::OpenFileSelector("*.png;*.tga;*.jpg;*.jpeg"); !filename.empty()) { normalMap = Texture2D::Create(filename); materialInstance->Set("u_NormalTexture", normalMap); @@ -412,8 +436,7 @@ namespace Prism } if (ImGui::IsItemClicked()) { - std::string filename = Application::Get().OpenFile(""); - if (filename != "") + if (std::string filename = FileSystem::OpenFileSelector("*.png;*.tga;*.jpg;*.jpeg"); !filename.empty()) { metalnessMap = Texture2D::Create(filename); materialInstance->Set("u_MetalnessTexture", metalnessMap); @@ -451,8 +474,7 @@ namespace Prism } if (ImGui::IsItemClicked()) { - std::string filename = Application::Get().OpenFile(""); - if (filename != "") + if (std::string filename = FileSystem::OpenFileSelector("*.png;*.tga;*.jpg;*.jpeg"); !filename.empty()) { roughnessMap = Texture2D::Create(filename); materialInstance->Set("u_RoughnessTexture", roughnessMap); @@ -493,31 +515,13 @@ namespace Prism UI::BeginPropertyGrid(); ImGui::AlignTextToFramePadding(); - auto& light = m_EditorScene->GetLight(); - UI::PropertySlider("Light Direction", light.Direction, -1.0f, 1.0f); - UI::PropertyColor("Light Radiance", light.Radiance); - UI::PropertySlider("Light Multiplier", light.Multiplier, 0.0f, 5.0f); - UI::PropertySlider("Exposure", m_EditorCamera.GetExposure(), 0.0f, 5.0f); - UI::Property("Radiance Prefiltering", m_RadiancePrefilter); - UI::PropertySlider("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f); - if (m_SceneState == SceneState::Edit) + float physics2DGravity = m_EditorScene->GetPhysics2DGravity(); + if (UI::Property("2D World Gravity", physics2DGravity, -100.0f, 100.0f)) { - float physics2DGravity = m_EditorScene->GetPhysics2DGravity(); - if (UI::Property("2D World Gravity", physics2DGravity, -10000.0f, 10000.0f)) - { - m_EditorScene->SetPhysics2DGravity(physics2DGravity); - } - } - else if (m_SceneState == SceneState::Play) - { - float physics2DGravity = m_RuntimeScene->GetPhysics2DGravity(); - if (UI::Property("2D World Gravity", physics2DGravity, -10000.0f, 10000.0f)) - { - m_RuntimeScene->SetPhysics2DGravity(physics2DGravity); - } + m_EditorScene->SetPhysics2DGravity(physics2DGravity); } UI::Property("Show Bounding Boxes", m_UIShowBoundingBoxes); @@ -579,6 +583,15 @@ namespace Prism m_ViewportPanelHovered = ImGui::IsWindowHovered(); m_ViewportPanelFocused = ImGui::IsWindowFocused(); + if (!m_ViewportPanelFocused && m_ViewportPanelHovered) + { + if (Input::IsMouseButtonPressed(MouseButton::Right)) + { + ImGui::SetWindowFocus("Viewport"); + m_ViewportPanelFocused = true; + } + } + auto viewportOffset = ImGui::GetCursorPos(); // includes tab bar auto viewportSize = ImGui::GetContentRegionAvail(); @@ -693,7 +706,7 @@ namespace Prism } }else { - glm::mat4 transformBase = transform * selection.Mesh->Transform; + glm::mat4 transformBase = transform * selection.Mesh->LocalTransform; ImGuizmo::Manipulate(glm::value_ptr(m_EditorCamera.GetViewMatrix()), glm::value_ptr(m_EditorCamera.GetProjectionMatrix()), (ImGuizmo::OPERATION)m_GizmoType, @@ -704,6 +717,7 @@ namespace Prism if (ImGuizmo::IsUsing()) { + selection.Mesh->LocalTransform = glm::inverse(entityTransform.GetTransform()) * transformBase; selection.Mesh->Transform = glm::inverse(entityTransform.GetTransform()) * transformBase; } } @@ -950,7 +964,7 @@ namespace Prism case KeyCode::B: // Toggle bounding boxes m_UIShowBoundingBoxes = !m_UIShowBoundingBoxes; - ShowBoundingBoxes(m_UIShowBoundingBoxes,false); + ShowBoundingBoxes(m_UIShowBoundingBoxes); break; case KeyCode::D: if (m_SelectionContext.size()) @@ -1059,9 +1073,9 @@ namespace Prism return false; } - void EditorLayer::ShowBoundingBoxes(bool show, bool onTop) + void EditorLayer::ShowBoundingBoxes(const bool show) { - SceneRenderer::GetOptions().ShowBoundingBoxes = show && !onTop; + SceneRenderer::GetOptions().ShowBoundingBoxes = show; } void EditorLayer::SelectEntity(Entity entity) @@ -1157,9 +1171,7 @@ namespace Prism void EditorLayer::OpenScene() { - const auto& app = Application::Get(); - const std::string filepath = app.OpenFile("Hazel Scene (*.hsc)\0*.hsc\0"); - if (!filepath.empty()) + if (const std::string filepath = FileSystem::OpenFileSelector("*.scene"); !filepath.empty()) OpenScene(filepath); } @@ -1199,8 +1211,7 @@ namespace Prism void EditorLayer::SaveSceneAs() { const auto& app = Application::Get(); - const std::string filepath = app.SaveFile("Prism Scene (*.hsc)\0*.hsc\0"); - if (!filepath.empty()) + if (const std::string filepath = FileSystem::SaveFileSelector("*.scene"); !filepath.empty()) { PM_CLIENT_INFO("Saving scene to: {0}", m_SceneFilePath); SceneSerializer serializer(m_EditorScene); diff --git a/Editor/Editor/EditorLayer.h b/Editor/Editor/EditorLayer.h index 3e49676..25dfbf2 100644 --- a/Editor/Editor/EditorLayer.h +++ b/Editor/Editor/EditorLayer.h @@ -28,7 +28,7 @@ namespace Prism bool OnKeyPressedEvent(KeyPressedEvent& e); bool OnMouseButtonPressedEvent(MouseButtonPressedEvent& e); - void ShowBoundingBoxes(bool show, bool onTop = false); + void ShowBoundingBoxes(bool show); void SelectEntity(Entity entity); void UpdateWindowTitle(const std::string& sceneName); @@ -126,10 +126,6 @@ namespace Prism // PBR params - bool m_RadiancePrefilter = false; - - float m_EnvMapRotation = 0.0f; - // Editor resources Ref m_CheckerboardTex; diff --git a/Prism/CMakeLists.txt b/Prism/CMakeLists.txt index 40eee3a..1e72f10 100644 --- a/Prism/CMakeLists.txt +++ b/Prism/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory(vendor/mono EXCLUDE_FROM_ALL) add_subdirectory(vendor/FastNoise EXCLUDE_FROM_ALL) add_subdirectory(vendor/yaml-cpp EXCLUDE_FROM_ALL) add_subdirectory(vendor/Box2D EXCLUDE_FROM_ALL) +add_subdirectory(vendor/efsw EXCLUDE_FROM_ALL) # ------------- imgui ------------- @@ -91,6 +92,7 @@ set(LINK_LIBRARIES_PRIVATE tinyFileDialogs FastNoise yaml-cpp + efsw PhysXExtensions_static_64 PhysX_static_64 diff --git a/Prism/src/Prism/Asset/AssetsManager.cpp b/Prism/src/Prism/Asset/AssetsManager.cpp index 2a73a16..8c29a63 100644 --- a/Prism/src/Prism/Asset/AssetsManager.cpp +++ b/Prism/src/Prism/Asset/AssetsManager.cpp @@ -303,11 +303,11 @@ namespace Prism return newFileName; } - void AssetsManager::ImportAsset(const std::string& filepath, AssetHandle parentHandle) + Ref AssetsManager::ImportAsset(const std::string& filepath, AssetHandle parentHandle) { const std::string extension = Utils::GetExtension(filepath); if (extension == "meta") - return; + return Ref(); const AssetType type = AssetTypes::GetAssetTypeFromExtension(extension); Ref asset = AssetSerializer::LoadAssetInfo(filepath, parentHandle, type); @@ -320,10 +320,12 @@ namespace Prism } s_LoadedAssets[asset->Handle] = asset; + + return asset; } - AssetHandle AssetsManager::ProcessDirectory(const std::string& directoryPath, AssetHandle parentHandle) + Ref AssetsManager::ProcessDirectory(const std::string& directoryPath, AssetHandle parentHandle) { Ref dirInfo = AssetSerializer::LoadAssetInfo(directoryPath, parentHandle, AssetType::Directory).As(); s_LoadedAssets[dirInfo->Handle] = dirInfo; @@ -336,15 +338,17 @@ namespace Prism if (entry.is_directory()) ProcessDirectory(entry.path().string(), dirInfo->Handle); else - ImportAsset(entry.path().string(), dirInfo->Handle); + { + Ref asset = ImportAsset(entry.path().string(), dirInfo->Handle); + } } - return dirInfo->Handle; + return dirInfo; } - void AssetsManager::ReloadAssets() + Ref AssetsManager::ReloadAssets() { - ProcessDirectory("assets", 0); + return ProcessDirectory("assets", 0); } void AssetsManager::OnFileSystemChanged(FileSystemChangedEvent e) @@ -363,35 +367,70 @@ namespace Prism if (e.IsDirectory) ProcessDirectory(e.FilePath, parentHandle); else - ImportAsset(e.FilePath, parentHandle); + { + Ref asset = ImportAsset(e.FilePath, parentHandle); + } + } break; case FileSystemAction::Modified: { if (!e.IsDirectory) - ImportAsset(e.FilePath, parentHandle); + { + Ref asset = ImportAsset(e.FilePath, parentHandle); + } } break; case FileSystemAction::Rename: { + Ref asset; + for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); it++) { if (it->second->FileName == e.OldName) { - it->second->FilePath = e.FilePath; - it->second->FileName = e.NewName; + asset = it->second; } } + + if (asset) + { + if (asset->Type != AssetTypes::GetAssetTypeFromExtension(Utils::GetExtension(e.FilePath))) + { + RemoveAsset(asset->Handle); + FileSystem::PrismDeleteFile(asset->FilePath + ".meta"); + asset = ImportAsset(e.FilePath, parentHandle); + }else + { + std::string oldMetaPath = asset->FilePath + ".meta"; + std::string newMetaPath = e.FilePath + ".meta"; + + std::error_code ec; + std::filesystem::rename(oldMetaPath, newMetaPath, ec); + if (ec) + { + PM_CORE_ERROR("Failed to rename meta file: {}", ec.message()); + } + + asset->FilePath = e.FilePath; + asset->FileName = e.NewName; + } + + } + } break; case FileSystemAction::Delete: { for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); it++) { - if (it->second->FilePath != e.FilePath) + std::string filePath = Utils::NormalizePath(it->second->FilePath); + std::string eFilePath = Utils::NormalizePath(e.FilePath); + if (filePath != eFilePath) continue; RemoveAsset(it->first); + FileSystem::PrismDeleteFile(eFilePath + ".meta"); break; } } diff --git a/Prism/src/Prism/Asset/AssetsManager.h b/Prism/src/Prism/Asset/AssetsManager.h index b0f10df..34b903b 100644 --- a/Prism/src/Prism/Asset/AssetsManager.h +++ b/Prism/src/Prism/Asset/AssetsManager.h @@ -10,6 +10,7 @@ #include "Asset.h" #include "Prism/Utilities/FileSystem.h" #include "Prism/Core/Ref.h" +#include "Prism/Project/Project.h" namespace Prism { @@ -66,9 +67,14 @@ namespace Prism private: - static void ImportAsset(const std::string& filepath, AssetHandle parentHandle); - static AssetHandle ProcessDirectory(const std::string& directoryPath, AssetHandle parentHandle); - static void ReloadAssets(); + static Ref ImportAsset(const std::string& filepath, AssetHandle parentHandle); + static Ref ProcessDirectory(const std::string& directoryPath, AssetHandle parentHandle); + /** + * this will load from projectPath all assets, and return the root Asset Ref + * @param projectPath assets path + * @return the root asset Ref + */ + static Ref ReloadAssets(); static void OnFileSystemChanged(FileSystemChangedEvent e); diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index e9c3b57..03815e2 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -10,7 +10,6 @@ #include "GLFW/glfw3.h" #include "Prism/Renderer/FrameBuffer.h" -#include "tinyfiledialogs.h" #include "Prism/Physics/Physics3D.h" #include "Prism/Script/ScriptEngine.h" #include "Prism/Asset/AssetsManager.h" @@ -28,6 +27,10 @@ namespace Prism Application::Application(const ApplicationProps& props) { + if (s_Instance != nullptr) + { + PM_CORE_ASSERT(false, "Application already exists!"); + } s_Instance = this; m_Window = std::unique_ptr(Window::Create(WindowProps{props.Name, props.Width, props.Height})); @@ -101,6 +104,9 @@ namespace Prism { m_ImGuiLayer->Begin(); + for (Layer* layer : m_LayerStack) + layer->OnImGuiRender(); + ImGui::Begin("Renderer"); const auto& caps = RendererAPI::GetCapabilities(); ImGui::Text("Vendor: %s", caps.Vendor.c_str()); @@ -109,90 +115,9 @@ namespace Prism ImGui::Text("Frame Time: %.2fms\n", m_TimeStep.GetMilliseconds()); ImGui::End(); - for (Layer* layer : m_LayerStack) - layer->OnImGuiRender(); - m_ImGuiLayer->End(); } - // TODO: fix this to prase filter - std::string Application::OpenFile(const std::string& filter) const - { - // TODO: will move it to other folder - // 处理过滤器 - std::vector filterPatterns; - std::vector patternStorage; - - if (!filter.empty()) { - const char* ptr = filter.c_str(); - bool isDescription = true; - - while (*ptr) { - if (isDescription) { - // 跳过描述 - isDescription = false; - } else { - // 添加模式 - patternStorage.push_back(ptr); - isDescription = true; - } - ptr += strlen(ptr) + 1; - } - - // 转换为 C 字符串数组 - for (const auto& pattern : patternStorage) { - filterPatterns.push_back(pattern.c_str()); - } - } - - // 如果没有过滤器,添加默认值 - if (filterPatterns.empty()) { - filterPatterns.push_back("*"); - } - - // 构建过滤器描述 - std::string filterDesc; - if (!patternStorage.empty()) { - std::stringstream ss; - ss << "Files ("; - for (size_t i = 0; i < patternStorage.size(); ++i) { - if (i > 0) ss << ","; - ss << patternStorage[i]; - } - ss << ")"; - filterDesc = ss.str(); - } - - const char* result = tinyfd_openFileDialog( - "Open File", // 对话框标题 - nullptr, // 初始路径,nullptr 表示使用默认 - 0, // 过滤器数量 (注意:这里需要根据解析情况调整,见下文) - nullptr, // 过滤器模式数组 - nullptr, // 过滤器描述 - 0 // 是否允许多选:0 为单选,1 为多选 - ); - - // 调用文件对话框 - /* - const char* result = tinyfd_openFileDialog( - "Open File", // 标题 - nullptr, // 初始目录 - static_cast(filterPatterns.size()), // 过滤器数量 - filterPatterns.data(), // 过滤器模式 - filterDesc.empty() ? nullptr : filterDesc.c_str(), // 描述 - 0 // 单选 - ); - */ - - return result ? std::string(result) : std::string(); - - } - - std::string Application::SaveFile(const std::string& filter) const - { - return OpenFile(filter); - } - Application& Application::Get() { return *s_Instance; diff --git a/Prism/src/Prism/Core/Application.h b/Prism/src/Prism/Core/Application.h index ae2fd61..61dfd25 100644 --- a/Prism/src/Prism/Core/Application.h +++ b/Prism/src/Prism/Core/Application.h @@ -13,10 +13,29 @@ namespace Prism { + class Project; + + struct CommandArgs + { + int count = 0; + char** args = nullptr; + + const char* operator[](const int index) + { + if (index >= count) + { + return nullptr; + } + return args[count]; + } + }; + + struct ApplicationProps { std::string Name; uint32_t Width, Height; + CommandArgs CommandArgs; }; class PRISM_API Application @@ -35,9 +54,6 @@ namespace Prism void RenderImGui(); - std::string OpenFile(const std::string& filter = "All\0*.*\0") const; - std::string SaveFile(const std::string& filter = "All\0*.*\0") const; - inline Window& GetWindow() { return *m_Window; } static Application& Get(); @@ -70,7 +86,7 @@ namespace Prism // this function will implemented by client - Application* CreateApplication(); + Application* CreateApplication(CommandArgs args); } #if defined(_MSC_VER) && !defined(__clang__) diff --git a/Prism/src/Prism/Core/EntryPoint.h b/Prism/src/Prism/Core/EntryPoint.h index ef55db9..ee29947 100644 --- a/Prism/src/Prism/Core/EntryPoint.h +++ b/Prism/src/Prism/Core/EntryPoint.h @@ -5,17 +5,26 @@ #ifndef ENTRYPOINT_H #define ENTRYPOINT_H +#include + #include "Application.h" -extern Prism::Application* Prism::CreateApplication(); +extern Prism::Application* Prism::CreateApplication(CommandArgs args); int main(int argc, char** argv) { + //TODO: this will use other method to impl + std::cout << std::filesystem::current_path() << std::endl; + std::filesystem::current_path(std::filesystem::path(argv[0]).parent_path()); + std::cout << std::filesystem::current_path() << std::endl; + #ifdef PRISM_STATIC Prism::InitializeCore(); #endif - Prism::Application* app = Prism::CreateApplication(); + const Prism::CommandArgs args{ argc, argv }; + + Prism::Application* app = Prism::CreateApplication(args); app->Run(); delete app; diff --git a/Prism/src/Prism/Core/Log.cpp b/Prism/src/Prism/Core/Log.cpp index 78374ff..c110300 100644 --- a/Prism/src/Prism/Core/Log.cpp +++ b/Prism/src/Prism/Core/Log.cpp @@ -10,9 +10,11 @@ #include "spdlog/sinks/stdout_color_sinks-inl.h" #ifdef _DEBUG -#define LOG_LEVEL spdlog::level::trace + #define LOG_LEVEL spdlog::level::trace + #define LOG_PATTERN "%^[%@:%#][%Y-%m-%d %H:%M:%S.%e] [%l] %v%$" #else #define LOG_LEVEL spdlog::level::trace + #define LOG_PATTERN "%^[%Y-%m-%d %H:%M:%S.%e] [%l] %v%$" #endif namespace Prism @@ -40,11 +42,11 @@ namespace Prism s_CoreLogger = std::make_shared("PRISM", prismSinks.begin(), prismSinks.end()); s_CoreLogger->set_level(LOG_LEVEL); - s_CoreLogger->set_pattern("%^[%Y-%m-%d %H:%M:%S.%e] [%l] %v%$"); + s_CoreLogger->set_pattern(LOG_PATTERN); s_ClientLogger = std::make_shared("APP", appSinks.begin(), appSinks.end()); s_ClientLogger->set_level(LOG_LEVEL); - s_CoreLogger->set_pattern("%^[%Y-%m-%d %H:%M:%S.%e] [%l] %v%$"); + s_ClientLogger->set_pattern(LOG_PATTERN); } void Log::Shutdown() diff --git a/Prism/src/Prism/Core/Log.h b/Prism/src/Prism/Core/Log.h index 41a0fa8..9e93014 100644 --- a/Prism/src/Prism/Core/Log.h +++ b/Prism/src/Prism/Core/Log.h @@ -25,13 +25,22 @@ namespace Prism }; } +#ifdef _DEBUG + #define PM_CORE_TRACE(...) SPDLOG_LOGGER_TRACE(::Prism::Log::GetCoreLogger(), __VA_ARGS__) + #define PM_CORE_DEBUG(...) SPDLOG_LOGGER_DEBUG(::Prism::Log::GetCoreLogger(), __VA_ARGS__) + #define PM_CORE_INFO(...) SPDLOG_LOGGER_INFO(::Prism::Log::GetCoreLogger(), __VA_ARGS__) + #define PM_CORE_WARN(...) SPDLOG_LOGGER_WARN(::Prism::Log::GetCoreLogger(), __VA_ARGS__) + #define PM_CORE_ERROR(...) SPDLOG_LOGGER_ERROR(::Prism::Log::GetCoreLogger(), __VA_ARGS__) + #define PM_CORE_FATAL(...) SPDLOG_LOGGER_CRITICAL(::Prism::Log::GetCoreLogger(), __VA_ARGS__) +#else + #define PM_CORE_TRACE(...) ::Prism::Log::GetCoreLogger()->trace(__VA_ARGS__) + #define PM_CORE_DEBUG(...) ::Prism::Log::GetCoreLogger()->debug( __VA_ARGS__) + #define PM_CORE_INFO(...) ::Prism::Log::GetCoreLogger()->info(__VA_ARGS__) + #define PM_CORE_WARN(...) ::Prism::Log::GetCoreLogger()->warn(__VA_ARGS__) + #define PM_CORE_ERROR(...) ::Prism::Log::GetCoreLogger()->error(__VA_ARGS__) + #define PM_CORE_FATAL(...) ::Prism::Log::GetCoreLogger()->critical(__VA_ARGS__) +#endif -#define PM_CORE_TRACE(...) ::Prism::Log::GetCoreLogger()->trace(__VA_ARGS__) -#define PM_CORE_DEBUG(...) ::Prism::Log::GetCoreLogger()->debug( __VA_ARGS__) -#define PM_CORE_INFO(...) ::Prism::Log::GetCoreLogger()->info(__VA_ARGS__) -#define PM_CORE_WARN(...) ::Prism::Log::GetCoreLogger()->warn(__VA_ARGS__) -#define PM_CORE_ERROR(...) ::Prism::Log::GetCoreLogger()->error(__VA_ARGS__) -#define PM_CORE_FATAL(...) ::Prism::Log::GetCoreLogger()->critical(__VA_ARGS__) #define PM_CLIENT_TRACE(...) ::Prism::Log::GetClientLogger()->trace(__VA_ARGS__) #define PM_CLIENT_DEBUG(...) ::Prism::Log::GetClientLogger()->debug(__VA_ARGS__) diff --git a/Prism/src/Prism/Editor/ContentBrowserPanel.cpp b/Prism/src/Prism/Editor/ContentBrowserPanel.cpp index 3c37eee..1c088f4 100644 --- a/Prism/src/Prism/Editor/ContentBrowserPanel.cpp +++ b/Prism/src/Prism/Editor/ContentBrowserPanel.cpp @@ -133,7 +133,7 @@ namespace Prism if (ImGui::MenuItem("Import")) { - //std::string filename = Application::Get().OpenFile(""); + // std::string filename = FileSystem::OpenFileSelector(); } if (ImGui::MenuItem("Refresh")) diff --git a/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp b/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp index 2adbad1..4526f64 100644 --- a/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp +++ b/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp @@ -9,16 +9,158 @@ #include #include +#include "tinyfiledialogs.h" +#include #include "Prism/Core/Log.h" #include "Prism/Asset/AssetsManager.h" +#include "Prism/Core/Application.h" + +#include "efsw/efsw.hpp" namespace Prism { FileSystem::FileSystemChangedCallbackFn FileSystem::s_Callback; - static bool s_Watching = true; - static bool s_IgnoreNextChange = false; - static HANDLE s_WatcherThread; + std::atomic FileSystem::s_IgnoreNextChange = false; + efsw::FileWatcher* FileSystem::s_FileWatcher = nullptr; + PrismFileWatcherListener* FileSystem::s_Listener = nullptr; + + class PrismFileWatcherListener : public efsw::FileWatchListener + { + public: + void handleFileAction(efsw::WatchID watchid, + const std::string& dir, + const std::string& filename, + efsw::Action action, + std::string oldFilename = "") override + { + // 如果引擎自身操作设置了忽略标志,则跳过本次事件 + if (FileSystem::s_IgnoreNextChange.load()) + return; + + std::filesystem::path fullPath = std::filesystem::path(dir) / filename; + + FileSystemChangedEvent e; + e.FilePath = fullPath.string(); + e.NewName = filename; + e.OldName = oldFilename; // efsw 在重命名时会提供旧文件名 + e.IsDirectory = false; // 稍后根据实际情况判断 + + // 判断是否为目录(可能抛出异常,使用 error_code 版本) + std::error_code ec; + if (std::filesystem::is_directory(fullPath, ec)) + e.IsDirectory = true; + // 如果 ec 有错误(文件可能已被删除),保持 false + + // 映射 efsw 动作到你的枚举 + switch (action) + { + case efsw::Actions::Add: + e.Action = FileSystemAction::Added; + break; + case efsw::Actions::Delete: + e.Action = FileSystemAction::Delete; + break; + case efsw::Actions::Modified: + e.Action = FileSystemAction::Modified; + break; + case efsw::Actions::Moved: + e.Action = FileSystemAction::Rename; + break; + default: + return; // 忽略未知事件 + } + + // 调用外部回调(需确保线程安全) + if (FileSystem::s_Callback) + FileSystem::s_Callback(e); + } + }; + + + std::string FileSystem::OpenFileSelector(const std::string& filter) + { + std::vector patternCstrs; + std::vector patterns; + std::string description = "All files (*.*)"; + + if (!filter.empty() && filter != "*.*") { + std::istringstream iss(filter); + std::string token; + while (std::getline(iss, token, ';')) { + if (!token.empty()) { + patterns.push_back(token); + } + } + + // 构建描述文字 + if (!patterns.empty()) { + description = "Custom files ("; + for (size_t i = 0; i < patterns.size(); ++i) { + if (i > 0) description += "; "; + description += patterns[i]; + } + description += ")"; + + // 准备 C 字符串数组 + for (const auto& p : patterns) { + patternCstrs.push_back(p.c_str()); + } + } + } + + const char* result = tinyfd_openFileDialog( + "Open File", // title + "", // default open path + static_cast(patternCstrs.size()), // filter count + patternCstrs.empty() ? nullptr : patternCstrs.data(), // filter pattern + description.c_str(), // filter description + 0 // 0 is single select, 1 is Multiple select + ); + + return result ? std::string(result) : std::string(); + } + + std::string FileSystem::SaveFileSelector(const std::string& filter) + { + std::vector patternCstrs; + std::string description = "All files (*.*)"; + std::vector patterns; + + if (!filter.empty() && filter != "*.*") { + std::istringstream iss(filter); + std::string token; + while (std::getline(iss, token, ';')) { + if (!token.empty()) { + patterns.push_back(token); + } + } + + if (!patterns.empty()) { + description = "Custom files ("; + for (size_t i = 0; i < patterns.size(); ++i) { + if (i > 0) description += "; "; + description += patterns[i]; + } + description += ")"; + + for (const auto& p : patterns) { + patternCstrs.push_back(p.c_str()); + } + } + } + + const char* result = tinyfd_saveFileDialog( + "Save File", + "", + static_cast(patternCstrs.size()), + patternCstrs.empty() ? nullptr : patternCstrs.data(), + description.c_str() + ); + + return result ? std::string(result) : std::string(); + } + bool FileSystem::CreateFolder(const std::filesystem::path& filepath) { @@ -54,57 +196,29 @@ namespace Prism s_Callback = callback; } - /* - static std::string wchar_to_string(wchar_t* input) - { - std::wstring string_input(input); - std::string converted(string_input.begin(), string_input.end()); - return converted; - } - */ - - static std::string wchar_to_string(const wchar_t* input) - { - if (!input) return {}; - - const int bufferSize = WideCharToMultiByte(CP_UTF8, 0, input, -1, nullptr, 0, nullptr, nullptr); - if (bufferSize == 0) return {}; - - std::string result(bufferSize, 0); - WideCharToMultiByte(CP_UTF8, 0, input, -1, &result[0], bufferSize, nullptr, nullptr); - - // 移除末尾的null终止符 - result.pop_back(); - return result; - } - std::string FileSystem::Rename(const std::string& filepath, const std::string& newName) { s_IgnoreNextChange = true; - std::filesystem::path p = filepath; + const std::filesystem::path p = filepath; std::string newFilePath = p.parent_path().string() + "/" + newName + p.extension().string(); MoveFileA(filepath.c_str(), newFilePath.c_str()); s_IgnoreNextChange = false; return newFilePath; } + bool FileSystem::PrismDeleteFile(const std::string& filepath) { s_IgnoreNextChange = true; - std::string fp = filepath; - fp.append(1, '\0'); - SHFILEOPSTRUCTA file_op; - file_op.hwnd = NULL; - file_op.wFunc = FO_DELETE; - file_op.pFrom = fp.c_str(); - file_op.pTo = ""; - file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; - file_op.fAnyOperationsAborted = false; - file_op.hNameMappings = 0; - file_op.lpszProgressTitle = ""; - const int result = SHFileOperationA(&file_op); + std::error_code ec; + const bool removed = std::filesystem::remove(filepath, ec); s_IgnoreNextChange = false; - return result == 0; + if (!removed || ec) + { + PM_CORE_ERROR("Delete failed: {}", ec.message()); + return false; + } + return true; } bool FileSystem::PrismMoveFile(const std::string& filepath, const std::string& dest) @@ -120,139 +234,41 @@ namespace Prism void FileSystem::StartWatching() { - DWORD threadId; - s_WatcherThread = CreateThread(NULL, 0, Watch, NULL, 0, &threadId); - PM_CORE_ASSERT(s_WatcherThread != NULL); - PM_CORE_TRACE("Starting file watching services"); + if (s_FileWatcher) return; + + s_FileWatcher = new efsw::FileWatcher(); + + s_Listener = new PrismFileWatcherListener(); + + efsw::WatchID watchID = s_FileWatcher->addWatch("assets", s_Listener, true); + if (watchID < 0) + { + PM_CORE_ERROR("Failed to add watch for {}", "assets"); + delete s_FileWatcher; + s_FileWatcher = nullptr; + delete s_Listener; + s_Listener = nullptr; + return; + } + + s_FileWatcher->watch(); + + PM_CORE_TRACE("Started file watching services on {}", "assets"); } void FileSystem::StopWatching() { - // TODO: this delay is too long - s_Watching = false; - const DWORD result = WaitForSingleObject(s_WatcherThread, 1000); - if (result == WAIT_TIMEOUT) - TerminateThread(s_WatcherThread, 0); - CloseHandle(s_WatcherThread); - PM_CORE_TRACE("closing file watching services"); - } - - unsigned long FileSystem::Watch(void* param) - { - const LPCSTR filepath = "assets"; - BYTE* buffer = new BYTE[10 * 1024]; // 1 MB - OVERLAPPED overlapped = { 0 }; - HANDLE handle = NULL; - DWORD bytesReturned = 0; - - handle = CreateFile( - filepath, - FILE_LIST_DIRECTORY, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, - NULL - ); - - ZeroMemory(&overlapped, sizeof(overlapped)); - - if (handle == INVALID_HANDLE_VALUE) - PM_CORE_ERROR("Unable to accquire directory handle: {0}", GetLastError()); - - overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - if (overlapped.hEvent == NULL) + if (s_FileWatcher) { - PM_CORE_ERROR("CreateEvent failed!"); - return 0; + delete s_FileWatcher; + s_FileWatcher = nullptr; } - - while (s_Watching) + if (s_Listener) { - DWORD status = ReadDirectoryChangesW( - handle, - buffer, - 10 * 1024 * sizeof(BYTE), - TRUE, - FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, - &bytesReturned, - &overlapped, - NULL - ); - - if (!status) - PM_CORE_ERROR("{0}", GetLastError()); - - const DWORD waitOperation = WaitForSingleObject(overlapped.hEvent, 2000); - if (waitOperation != WAIT_OBJECT_0) - continue; - - if (s_IgnoreNextChange) - continue; - - std::string oldName; - char fileName[MAX_PATH * 10] = ""; - - FILE_NOTIFY_INFORMATION* current = (FILE_NOTIFY_INFORMATION*)buffer; - - for (;;) - { - ZeroMemory(fileName, sizeof(fileName)); - - WideCharToMultiByte(CP_ACP, 0, current->FileName, current->FileNameLength / sizeof(WCHAR), fileName, sizeof(fileName), NULL, NULL); - std::filesystem::path filePath = std::filesystem::path("assets") / std::string(fileName); - - FileSystemChangedEvent e; - e.FilePath = filePath.string(); - e.NewName = filePath.filename().string(); - e.OldName = filePath.filename().string(); - e.IsDirectory = std::filesystem::is_directory(filePath); - - switch (current->Action) - { - case FILE_ACTION_ADDED: - { - e.Action = FileSystemAction::Added; - s_Callback(e); - break; - } - case FILE_ACTION_REMOVED: - { - e.IsDirectory = AssetsManager::IsDirectory(e.FilePath); - e.Action = FileSystemAction::Delete; - s_Callback(e); - break; - } - case FILE_ACTION_MODIFIED: - { - e.Action = FileSystemAction::Modified; - s_Callback(e); - break; - } - case FILE_ACTION_RENAMED_OLD_NAME: - { - oldName = filePath.filename().string(); - break; - } - case FILE_ACTION_RENAMED_NEW_NAME: - { - e.OldName = oldName; - e.Action = FileSystemAction::Rename; - s_Callback(e); - break; - } - } - - if (!current->NextEntryOffset) - break; - - current += current->NextEntryOffset; - } + delete s_Listener; + s_Listener = nullptr; } - - return 0; + PM_CORE_TRACE("Stopped file watching services"); } - } diff --git a/Prism/src/Prism/Renderer/Renderer3D.cpp b/Prism/src/Prism/Renderer/Renderer3D.cpp index 88f213b..35c9d6a 100644 --- a/Prism/src/Prism/Renderer/Renderer3D.cpp +++ b/Prism/src/Prism/Renderer/Renderer3D.cpp @@ -163,7 +163,7 @@ namespace Prism //////////////// BloomPass //////////////// { FramebufferSpecification bloomBlurFramebufferSpec; - bloomBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F , FramebufferTextureFormat::RGBA8}; + bloomBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F , FramebufferTextureFormat::RGBA16F}; bloomBlurFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; RenderPassSpecification bloomBlurRenderPassSpec; @@ -173,7 +173,7 @@ namespace Prism s_Data.BloomBlurPass[1] = RenderPass::Create(bloomBlurRenderPassSpec); FramebufferSpecification bloomBlendFramebufferSpec; - bloomBlendFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 }; + bloomBlendFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F }; bloomBlendFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; RenderPassSpecification bloomBlendRenderPassSpec; @@ -275,7 +275,6 @@ namespace Prism s_Data.SceneData.SkyboxMaterial = scene->m_SkyboxMaterial; s_Data.SceneData.SceneEnvironment = scene->m_Environment; s_Data.SceneData.SceneEnvironmentIntensity = scene->m_EnvironmentIntensity; - s_Data.SceneData.ActiveLight = scene->m_Light; s_Data.SceneData.SceneLightEnvironment = scene->m_LightEnvironment; } diff --git a/Prism/src/Prism/Renderer/SceneRenderer.cpp b/Prism/src/Prism/Renderer/SceneRenderer.cpp index 2b755b4..dae0265 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.cpp +++ b/Prism/src/Prism/Renderer/SceneRenderer.cpp @@ -26,7 +26,7 @@ namespace Prism void SceneRenderer::Init() { FramebufferSpecification finalFramebufferSpec; - finalFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::Depth}; + finalFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::Depth}; finalFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; RenderPassSpecification finalRenderPassSpec; diff --git a/Prism/src/Prism/Scene/Scene.cpp b/Prism/src/Prism/Scene/Scene.cpp index ecd3119..28a5590 100644 --- a/Prism/src/Prism/Scene/Scene.cpp +++ b/Prism/src/Prism/Scene/Scene.cpp @@ -594,9 +594,6 @@ namespace Prism void Scene::CopyTo(Ref& target) { - target->m_Light = m_Light; - target->m_LightMultiplier = m_LightMultiplier; - target->m_Environment = m_Environment; target->m_SkyboxTexture = m_SkyboxTexture; target->m_SkyboxMaterial = m_SkyboxMaterial; @@ -658,6 +655,11 @@ namespace Prism return gravity.y; } + void Scene::SetPhysics2DGravity(const glm::vec2& vec) + { + b2World_SetGravity(m_Registry.get(m_SceneEntity).World, {vec.x, vec.y}); + } + void Scene::SetPhysics2DGravity(const float gravity) { b2World_SetGravity(m_Registry.get(m_SceneEntity).World, {0.0f, gravity}); diff --git a/Prism/src/Prism/Scene/Scene.h b/Prism/src/Prism/Scene/Scene.h index fb6b79d..8e642f3 100644 --- a/Prism/src/Prism/Scene/Scene.h +++ b/Prism/src/Prism/Scene/Scene.h @@ -75,9 +75,6 @@ namespace Prism float& GetSkyboxLod() { return m_SkyboxLod; } - Light& GetLight() { return m_Light; } - const Light& GetLight() const { return m_Light; } - Entity GetMainCameraEntity(); void AddEntity(Entity* entity); @@ -107,6 +104,16 @@ namespace Prism static Ref GetScene(const UUID& uuid); float GetPhysics2DGravity() const; + /** + * set 2D gravity two directions + * @param vec (x,y) two directory + */ + void SetPhysics2DGravity(const glm::vec2& vec); + + /** + * only set y-axis gravity + * @param gravity y gravity + */ void SetPhysics2DGravity(float gravity); // Editor-specific @@ -129,8 +136,6 @@ namespace Prism Entity* m_Physics2DBodyEntityBuffer = nullptr; Entity* m_Physics3DBodyEntityBuffer = nullptr; - Light m_Light; - float m_LightMultiplier = 0.3f; LightEnvironment m_LightEnvironment; bool m_IsPlaying = false; diff --git a/Prism/src/Prism/Scene/SceneSerializer.cpp b/Prism/src/Prism/Scene/SceneSerializer.cpp index a4c7835..6aff2c2 100644 --- a/Prism/src/Prism/Scene/SceneSerializer.cpp +++ b/Prism/src/Prism/Scene/SceneSerializer.cpp @@ -588,13 +588,6 @@ namespace Prism out << YAML::Value; out << YAML::BeginMap; // Environment out << YAML::Key << "AssetHandle" << YAML::Value << scene->GetEnvironment()->Handle; - const auto& light = scene->GetLight(); - out << YAML::Key << "Light" << YAML::Value; - out << YAML::BeginMap; // Light - out << YAML::Key << "Direction" << YAML::Value << light.Direction; - out << YAML::Key << "Radiance" << YAML::Value << light.Radiance; - out << YAML::Key << "Multiplier" << YAML::Value << light.Multiplier; - out << YAML::EndMap; // Light out << YAML::EndMap; // Environment } diff --git a/Prism/src/Prism/Utilities/FileSystem.h b/Prism/src/Prism/Utilities/FileSystem.h index 444c1e9..9aee3db 100644 --- a/Prism/src/Prism/Utilities/FileSystem.h +++ b/Prism/src/Prism/Utilities/FileSystem.h @@ -7,6 +7,12 @@ #include + +namespace efsw +{ + class FileWatcher; +} + namespace Prism { enum class FileSystemAction @@ -29,6 +35,9 @@ namespace Prism using FileSystemChangedCallbackFn = std::function; public: + static std::string OpenFileSelector(const std::string& filter = ""); + static std::string SaveFileSelector(const std::string& filter = ""); + static bool CreateFolder(const std::filesystem::path& filepath); static bool Exists(const std::string& filepath); static std::string Rename(const std::string& filepath, const std::string& newName); @@ -40,10 +49,13 @@ namespace Prism static void StopWatching(); private: - static unsigned long Watch(void* param); + static std::atomic s_IgnoreNextChange; + static efsw::FileWatcher* s_FileWatcher; + static class PrismFileWatcherListener* s_Listener; + static FileSystemChangedCallbackFn s_Callback; private: - static FileSystemChangedCallbackFn s_Callback; + friend class PrismFileWatcherListener; }; } diff --git a/Prism/src/Prism/Utilities/StringUtils.cpp b/Prism/src/Prism/Utilities/StringUtils.cpp index 7cd1426..1c0b8b1 100644 --- a/Prism/src/Prism/Utilities/StringUtils.cpp +++ b/Prism/src/Prism/Utilities/StringUtils.cpp @@ -17,13 +17,10 @@ namespace Prism::Utils return ""; } - std::string GetExtension(const std::string& filename) + std::string GetExtension(const std::string& filename, const bool includeDot) { - std::vector parts = SplitString(filename, '.'); - - if (parts.size() > 1) - return parts[parts.size() - 1]; - + if (const std::vector parts = SplitString(filename, '.'); parts.size() > 1) + return parts[ includeDot ? parts.size() - 1 : parts.size()]; return ""; } diff --git a/Prism/src/Prism/Utilities/StringUtils.h b/Prism/src/Prism/Utilities/StringUtils.h index b4ccb1f..b032193 100644 --- a/Prism/src/Prism/Utilities/StringUtils.h +++ b/Prism/src/Prism/Utilities/StringUtils.h @@ -10,7 +10,7 @@ namespace Prism::Utils { std::string GetFilename(const std::string& filepath); - std::string GetExtension(const std::string& filename); + std::string GetExtension(const std::string& filename, bool includeDot = true); std::string RemoveExtension(const std::string& filename); std::string NormalizePath(std::string path); std::string StringToLower(const std::string& str); diff --git a/Prism/vendor/efsw b/Prism/vendor/efsw new file mode 160000 index 0000000..22f17a0 --- /dev/null +++ b/Prism/vendor/efsw @@ -0,0 +1 @@ +Subproject commit 22f17a0bcdf3a4edf61f8b14328391463389e548