From 512cb3336ceb73e1d5ff6bd5f66ae2b485d4f23d Mon Sep 17 00:00:00 2001 From: Antoine Pilote Date: Fri, 19 Apr 2024 00:03:59 -0400 Subject: [PATCH] Partial implementation of new entity accesors from scene tree path --- Nuake/src/Scene/Scene.cpp | 23 ++ Nuake/src/Scene/Scene.h | 3 +- .../src/Scripting/NetModules/SceneNetAPI.cpp | 17 + Nuake/src/Vendors/imgui/imgui_notify.h | 361 ++++++++++++++++++ NuakeNet/src/Entity.cs | 24 +- 5 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 Nuake/src/Vendors/imgui/imgui_notify.h diff --git a/Nuake/src/Scene/Scene.cpp b/Nuake/src/Scene/Scene.cpp index a359ba39..0b31af8f 100644 --- a/Nuake/src/Scene/Scene.cpp +++ b/Nuake/src/Scene/Scene.cpp @@ -108,6 +108,29 @@ namespace Nuake return Entity{ (entt::entity)0, this }; } + Entity Scene::GetEntityFromPath(const std::string& path) + { + auto splits = String::Split(path, '/'); + + std::vector rootEntities; + const auto& view = m_Registry.view(); + for (const auto& e : view) + { + auto [parent, name] = view.get(e); + if (!parent.HasParent) + { + rootEntities.push_back({ e, this }); + } + } + + return Entity(); + } + + Entity Scene::GetRelativeEntityFromPath(Entity entity, const std::string& path) + { + return Entity(); + } + bool Scene::OnInit() { for (auto& system : m_Systems) diff --git a/Nuake/src/Scene/Scene.h b/Nuake/src/Scene/Scene.h index e426cb83..06fbaefd 100644 --- a/Nuake/src/Scene/Scene.h +++ b/Nuake/src/Scene/Scene.h @@ -74,6 +74,8 @@ namespace Nuake Entity GetEntity(const std::string& name); Entity GetEntity(int handle); Entity GetEntityByID(int id); + Entity GetEntityFromPath(const std::string& path); + Entity GetRelativeEntityFromPath(Entity entity, const std::string& path); template static void CopyComponent(entt::registry& dst, entt::registry& src); @@ -81,7 +83,6 @@ namespace Nuake Ref GetEnvironment() const; void SetEnvironment(Ref env); - bool Save(); bool SaveAs(const std::string& path); Ref Copy(); diff --git a/Nuake/src/Scripting/NetModules/SceneNetAPI.cpp b/Nuake/src/Scripting/NetModules/SceneNetAPI.cpp index bf1b4900..ba05fa31 100644 --- a/Nuake/src/Scripting/NetModules/SceneNetAPI.cpp +++ b/Nuake/src/Scripting/NetModules/SceneNetAPI.cpp @@ -150,6 +150,20 @@ namespace Nuake { return ScriptingEngineNet::Get().HasEntityScriptInstance(entity); } + int EntityGetEntity(int handle, Coral::String input) + { + std::string path = input; + + Ref scene = Engine::GetCurrentScene(); + if (String::BeginsWith(input, "/")) + { + return scene->GetEntityFromPath(input).GetHandle(); + } + + Entity entity = { (entt::entity)handle, scene.get() }; + return scene->GetRelativeEntityFromPath(entity, input).GetHandle(); + } + void TransformSetPosition(int entityId, float x, float y, float z) { Entity entity = { (entt::entity)(entityId), Engine::GetCurrentScene().get() }; @@ -275,8 +289,11 @@ namespace Nuake { void Nuake::SceneNetAPI::RegisterMethods() { + // Entity RegisterMethod("Entity.EntityHasComponentIcall", &EntityHasComponent); RegisterMethod("Entity.EntityHasManagedInstanceIcall", &EntityHasManagedInstance); + RegisterMethod("Entity.EntityGetEntityIcall", &EntityGetEntity); + // Scene RegisterMethod("Scene.GetEntityIcall", &GetEntity); RegisterMethod("Scene.GetEntityScriptFromNameIcall", &GetEntityScriptFromName); RegisterMethod("Scene.GetEntityScriptFromHandleIcall", &GetEntityScriptFromHandle); diff --git a/Nuake/src/Vendors/imgui/imgui_notify.h b/Nuake/src/Vendors/imgui/imgui_notify.h new file mode 100644 index 00000000..b0894c12 --- /dev/null +++ b/Nuake/src/Vendors/imgui/imgui_notify.h @@ -0,0 +1,361 @@ +// imgui-notify by patrickcjk +// https://github.com/patrickcjk/imgui-notify + +#ifndef IMGUI_NOTIFY +#define IMGUI_NOTIFY + +#pragma once +#include +#include +#include "src/Resource/FontAwesome5.h" +#include +#include + +#define NOTIFY_MAX_MSG_LENGTH 4096 // Max message content length +#define NOTIFY_PADDING_X 20.f // Bottom-left X padding +#define NOTIFY_PADDING_Y 20.f // Bottom-left Y padding +#define NOTIFY_PADDING_MESSAGE_Y 10.f // Padding Y between each message +#define NOTIFY_FADE_IN_OUT_TIME 150 // Fade in and out duration +#define NOTIFY_DEFAULT_DISMISS 3000 // Auto dismiss after X ms (default, applied only of no data provided in constructors) +#define NOTIFY_OPACITY 1.0f // 0-1 Toast opacity +#define NOTIFY_TOAST_FLAGS ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing +// Comment out if you don't want any separator between title and content +#define NOTIFY_USE_SEPARATOR + +#define NOTIFY_INLINE inline +#define NOTIFY_NULL_OR_EMPTY(str) (!str ||! strlen(str)) +#define NOTIFY_FORMAT(fn, format, ...) if (format) { va_list args; va_start(args, format); fn(format, args, __VA_ARGS__); va_end(args); } + +typedef int ImGuiToastType; +typedef int ImGuiToastPhase; +typedef int ImGuiToastPos; + +enum ImGuiToastType_ +{ + ImGuiToastType_None, + ImGuiToastType_Success, + ImGuiToastType_Warning, + ImGuiToastType_Error, + ImGuiToastType_Info, + ImGuiToastType_COUNT +}; + +enum ImGuiToastPhase_ +{ + ImGuiToastPhase_FadeIn, + ImGuiToastPhase_Wait, + ImGuiToastPhase_FadeOut, + ImGuiToastPhase_Expired, + ImGuiToastPhase_COUNT +}; + +enum ImGuiToastPos_ +{ + ImGuiToastPos_TopLeft, + ImGuiToastPos_TopCenter, + ImGuiToastPos_TopRight, + ImGuiToastPos_BottomLeft, + ImGuiToastPos_BottomCenter, + ImGuiToastPos_BottomRight, + ImGuiToastPos_Center, + ImGuiToastPos_COUNT +}; + +class ImGuiToast +{ +private: + ImGuiToastType type = ImGuiToastType_None; + char title[NOTIFY_MAX_MSG_LENGTH]; + char content[NOTIFY_MAX_MSG_LENGTH]; + int dismiss_time = NOTIFY_DEFAULT_DISMISS; + uint64_t creation_time = 0; + +private: + // Setters + + NOTIFY_INLINE auto set_title(const char* format, va_list args) { vsnprintf(this->title, sizeof(this->title), format, args); } + + NOTIFY_INLINE auto set_content(const char* format, va_list args) { vsnprintf(this->content, sizeof(this->content), format, args); } + +public: + + NOTIFY_INLINE auto set_title(const char* format, ...) -> void { NOTIFY_FORMAT(this->set_title, format); } + + NOTIFY_INLINE auto set_content(const char* format, ...) -> void { NOTIFY_FORMAT(this->set_content, format); } + + NOTIFY_INLINE auto set_type(const ImGuiToastType& type) -> void { IM_ASSERT(type < ImGuiToastType_COUNT); this->type = type; }; + +public: + // Getters + + NOTIFY_INLINE auto get_title() -> char* { return this->title; }; + + NOTIFY_INLINE auto get_default_title() -> char* + { + if (!strlen(this->title)) + { + switch (this->type) + { + case ImGuiToastType_None: + return NULL; + case ImGuiToastType_Success: + return "Success"; + case ImGuiToastType_Warning: + return "Warning"; + case ImGuiToastType_Error: + return "Error"; + case ImGuiToastType_Info: + return "Info"; + } + } + + return this->title; + }; + + NOTIFY_INLINE auto get_type() -> const ImGuiToastType& { return this->type; }; + + NOTIFY_INLINE auto get_color() -> const ImVec4& + { + switch (this->type) + { + case ImGuiToastType_None: + return { 255, 255, 255, 255 }; // White + case ImGuiToastType_Success: + return { 0, 255, 0, 255 }; // Green + case ImGuiToastType_Warning: + return { 255, 255, 0, 255 }; // Yellow + case ImGuiToastType_Error: + return { 255, 0, 0, 255 }; // Error + case ImGuiToastType_Info: + return { 0, 157, 255, 255 }; // Blue + } + } + + NOTIFY_INLINE auto get_icon() -> const char* + { + switch (this->type) + { + case ImGuiToastType_None: + return NULL; + case ImGuiToastType_Success: + return ICON_FA_CHECK_CIRCLE; + case ImGuiToastType_Warning: + return ICON_FA_EXCLAMATION_TRIANGLE; + case ImGuiToastType_Error: + return ICON_FA_TIMES_CIRCLE; + case ImGuiToastType_Info: + return ICON_FA_INFO_CIRCLE; + } + } + + NOTIFY_INLINE auto get_content() -> char* { return this->content; }; + + NOTIFY_INLINE auto get_elapsed_time() { return GetTickCount64() - this->creation_time; } + + NOTIFY_INLINE auto get_phase() -> const ImGuiToastPhase& + { + const auto elapsed = get_elapsed_time(); + + if (elapsed > NOTIFY_FADE_IN_OUT_TIME + this->dismiss_time + NOTIFY_FADE_IN_OUT_TIME) + { + return ImGuiToastPhase_Expired; + } + else if (elapsed > NOTIFY_FADE_IN_OUT_TIME + this->dismiss_time) + { + return ImGuiToastPhase_FadeOut; + } + else if (elapsed > NOTIFY_FADE_IN_OUT_TIME) + { + return ImGuiToastPhase_Wait; + } + else + { + return ImGuiToastPhase_FadeIn; + } + } + + NOTIFY_INLINE auto get_fade_percent() -> const float + { + const auto phase = get_phase(); + const auto elapsed = get_elapsed_time(); + + if (phase == ImGuiToastPhase_FadeIn) + { + return ((float)elapsed / (float)NOTIFY_FADE_IN_OUT_TIME) * NOTIFY_OPACITY; + } + else if (phase == ImGuiToastPhase_FadeOut) + { + return (1.f - (((float)elapsed - (float)NOTIFY_FADE_IN_OUT_TIME - (float)this->dismiss_time) / (float)NOTIFY_FADE_IN_OUT_TIME)) * NOTIFY_OPACITY; + } + + return 1.f * NOTIFY_OPACITY; + } + +public: + // Constructors + + ImGuiToast(ImGuiToastType type, int dismiss_time = NOTIFY_DEFAULT_DISMISS) + { + IM_ASSERT(type < ImGuiToastType_COUNT); + + this->type = type; + this->dismiss_time = dismiss_time; + this->creation_time = GetTickCount64(); + + memset(this->title, 0, sizeof(this->title)); + memset(this->content, 0, sizeof(this->content)); + } + + ImGuiToast(ImGuiToastType type, const char* format, ...) : ImGuiToast(type) { NOTIFY_FORMAT(this->set_content, format); } + + ImGuiToast(ImGuiToastType type, int dismiss_time, const char* format, ...) : ImGuiToast(type, dismiss_time) { NOTIFY_FORMAT(this->set_content, format); } +}; + +namespace ImGui +{ + NOTIFY_INLINE std::vector notifications; + + /// + /// Insert a new toast in the list + /// + NOTIFY_INLINE VOID InsertNotification(const ImGuiToast& toast) + { + notifications.push_back(toast); + } + + /// + /// Remove a toast from the list by its index + /// + /// index of the toast to remove + NOTIFY_INLINE VOID RemoveNotification(int index) + { + notifications.erase(notifications.begin() + index); + } + + /// + /// Render toasts, call at the end of your rendering! + /// + NOTIFY_INLINE VOID RenderNotifications() + { + auto vp_size = GetMainViewport()->Size; + bool ViewportsEnable = false; + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + vp_size = ImGui::GetViewportPlatformMonitor(ImGui::GetMainViewport())->MainSize; + ViewportsEnable = true; + } + + float height = 0.f; + + for (auto i = 0; i < notifications.size(); i++) + { + auto* current_toast = ¬ifications[i]; + + // Remove toast if expired + if (current_toast->get_phase() == ImGuiToastPhase_Expired) + { + RemoveNotification(i); + continue; + } + + // Get icon, title and other data + const auto icon = current_toast->get_icon(); + const auto title = current_toast->get_title(); + const auto content = current_toast->get_content(); + const auto default_title = current_toast->get_default_title(); + const auto opacity = current_toast->get_fade_percent(); // Get opacity based of the current phase + + // Window rendering + auto text_color = current_toast->get_color(); + text_color.w = opacity; + + // Generate new unique name for this toast + char window_name[50]; + sprintf_s(window_name, "##TOAST%d", i); + + //PushStyleColor(ImGuiCol_Text, text_color); + SetNextWindowBgAlpha(opacity); + SetNextWindowPos(ImVec2(vp_size.x - NOTIFY_PADDING_X, vp_size.y - NOTIFY_PADDING_Y - height), ImGuiCond_Always, ImVec2(1.0f, 1.0f)); + Begin(window_name, NULL, NOTIFY_TOAST_FLAGS); + + // Here we render the toast content + { + PushTextWrapPos(vp_size.x / 3.f); // We want to support multi-line text, this will wrap the text after 1/3 of the screen width + + bool was_title_rendered = false; + + // If an icon is set + if (!NOTIFY_NULL_OR_EMPTY(icon)) + { + //Text(icon); // Render icon text + TextColored(text_color, icon); + was_title_rendered = true; + } + + // If a title is set + if (!NOTIFY_NULL_OR_EMPTY(title)) + { + // If a title and an icon is set, we want to render on same line + if (!NOTIFY_NULL_OR_EMPTY(icon)) + SameLine(); + + Text(title); // Render title text + was_title_rendered = true; + } + else if (!NOTIFY_NULL_OR_EMPTY(default_title)) + { + if (!NOTIFY_NULL_OR_EMPTY(icon)) + SameLine(); + + Text(default_title); // Render default title text (ImGuiToastType_Success -> "Success", etc...) + was_title_rendered = true; + } + + // In case ANYTHING was rendered in the top, we want to add a small padding so the text (or icon) looks centered vertically + if (was_title_rendered && !NOTIFY_NULL_OR_EMPTY(content)) + { + SetCursorPosY(GetCursorPosY() + 5.f); // Must be a better way to do this!!!! + } + + // If a content is set + if (!NOTIFY_NULL_OR_EMPTY(content)) + { + if (was_title_rendered) + { +#ifdef NOTIFY_USE_SEPARATOR + Separator(); +#endif + } + + Text(content); // Render content text + } + + PopTextWrapPos(); + } + + // Save height for next toasts + height += GetWindowHeight() + NOTIFY_PADDING_MESSAGE_Y; + + // End + End(); + } + } + + /// + /// Adds font-awesome font, must be called ONCE on initialization + /// Fonts are loaded from read-only memory, should be set to false! + /// + NOTIFY_INLINE VOID MergeIconsWithLatestFont(float font_size, bool FontDataOwnedByAtlas = false) + { + static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; + + ImFontConfig icons_config; + icons_config.MergeMode = true; + icons_config.PixelSnapH = true; + icons_config.FontDataOwnedByAtlas = FontDataOwnedByAtlas; + + //GetIO().Fonts->AddFontFromMemoryTTF((void*)fa_solid_900, sizeof(fa_solid_900), font_size, &icons_config, icons_ranges); + } +} + +#endif \ No newline at end of file diff --git a/NuakeNet/src/Entity.cs b/NuakeNet/src/Entity.cs index c10ce899..d16a8acf 100644 --- a/NuakeNet/src/Entity.cs +++ b/NuakeNet/src/Entity.cs @@ -1,6 +1,7 @@ using Coral.Managed.Interop; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Numerics; using System.Text; @@ -20,7 +21,7 @@ namespace Nuake.Net { internal static unsafe delegate* EntityHasComponentIcall; internal static unsafe delegate* EntityHasManagedInstanceIcall; - + internal static unsafe delegate* EntityGetEntityIcall; public enum ComponentTypes { Unknown = -1, @@ -122,5 +123,26 @@ namespace Nuake.Net return null; } + + public Entity? GetEntity(string path) where T : Entity + { + Entity entityInstance; + unsafe + { + int entityHandle = EntityGetEntityIcall(ECSHandle, path ); + + bool hasInstance = EntityHasManagedInstanceIcall(entityHandle); + if (hasInstance) + { + entityInstance = Scene.GetEntity(entityHandle); + } + else + { + entityInstance = new Entity { ECSHandle = entityHandle }; + } + } + + return null; + } } }