9#include "components/ErrorUtils.hpp"
10#include "components/PathUtils.hpp"
12#include "components/ColorTypes.hpp"
14#include <nlohmann/json.hpp>
16#include <unordered_map>
22#include <ImReflect.hpp>
25 ImTextureID GetEditorIcon_Internal(
const std::string &name);
29 void DrawAssetSlot(
const char *label, T &value, ImTextureID icon,
const char *hint)
33 float thumbnailSize = 32.0f;
34 std::string ext = std::filesystem::path(value.c_str()).extension().string();
35 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
38 if constexpr (std::is_same_v<T, vex::mesh_asset_path>)
40 good = vex::AssetExtensions::IsValid(ext, vex::AssetExtensions::Mesh);
42 else if constexpr (std::is_same_v<T, vex::texture_asset_path>)
44 good = vex::AssetExtensions::IsValid(ext, vex::AssetExtensions::Texture);
46 else if constexpr (std::is_same_v<T, vex::audio_asset_path>)
48 good = vex::AssetExtensions::IsValid(ext, vex::AssetExtensions::Audio);
51 ImTextureID fileIcon = vex::GetEditorIcon_Internal(
"file");
52 ImTextureID drawIcon = (value.empty()) ? fileIcon : icon;
60 if (ImGui::ImageButton(
"##AssetIcon", drawIcon, {thumbnailSize, thumbnailSize}, ImVec2(0, 0), ImVec2(1, 1)))
68 static ImGuiID activeAssetID = 0;
69 ImGuiID currentID = ImGui::GetID(
"##PathInput");
71 if (activeAssetID == currentID)
73 if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))
75 ImGui::SetKeyboardFocusHere(0);
78 ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
80 if (ImGui::InputText(
"##PathInput", (std::string *)&value, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll))
85 if (!ImGui::IsItemActive() && (ImGui::IsMouseClicked(ImGuiMouseButton_Left) || ImGui::IsMouseClicked(ImGuiMouseButton_Right)))
92 static std::unordered_map<std::string, std::pair<bool, double>> fileCache;
93 bool fileExists =
true;
97 double currentTime = ImGui::GetTime();
98 auto &entry = fileCache[value];
100 if (currentTime - entry.second > 5.0f)
103 entry.second = currentTime;
105 fileExists = entry.first;
108 std::string displayStr;
109 bool colorPushed =
false;
112 displayStr =
"Empty (" + std::string(hint) +
")";
113 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
118 displayStr = std::filesystem::path(value.c_str()).filename().string();
119 if (!fileExists || !good)
121 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.1f, 0.1f, 1.0f));
126 if (ImGui::Selectable(displayStr.c_str(),
false, ImGuiSelectableFlags_AllowDoubleClick))
128 activeAssetID = currentID;
133 ImGui::PopStyleColor();
136 if (!value.empty() && ImGui::IsItemHovered())
138 ImGui::SetTooltip(
"%s", value.c_str());
143 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.47f, 0.05f, 0.05f, 1.0f));
144 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.71f, 0.10f, 0.10f, 1.0f));
145 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.30f, 0.03f, 0.03f, 1.0f));
146 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.94f, 0.85f, 0.85f, 1.0f));
148 if (ImGui::Button(
"Clear", ImVec2(0, 0)))
153 ImGui::PopStyleColor(4);
156 if (ImGui::BeginDragDropTarget())
158 if (
const ImGuiPayload *payload = ImGui::AcceptDragDropPayload(
"ASSET_ITEM"))
160 std::string fullPath = (
const char *)payload->Data;
162 std::string ext = std::filesystem::path(fullPath).extension().string();
163 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
167 if constexpr (std::is_same_v<T, vex::mesh_asset_path>)
169 valid = vex::AssetExtensions::IsValid(ext, vex::AssetExtensions::Mesh);
171 else if constexpr (std::is_same_v<T, vex::texture_asset_path>)
173 valid = vex::AssetExtensions::IsValid(ext, vex::AssetExtensions::Texture);
175 else if constexpr (std::is_same_v<T, vex::audio_asset_path>)
177 valid = vex::AssetExtensions::IsValid(ext, vex::AssetExtensions::Audio);
185 ImGui::EndDragDropTarget();
190 inline void tag_invoke(ImReflect::ImInput_t,
const char *label,
vex::mesh_asset_path &value, ImSettings &settings, ImResponse &response)
192 DrawAssetSlot(label, value, vex::GetEditorIcon_Internal(
"mesh"),
"Mesh");
195 inline void tag_invoke(ImReflect::ImInput_t,
const char *label,
vex::texture_asset_path &value, ImSettings &settings, ImResponse &response)
197 DrawAssetSlot(label, value, vex::GetEditorIcon_Internal(
"texture"),
"Texture");
200 inline void tag_invoke(ImReflect::ImInput_t,
const char *label,
vex::audio_asset_path &value, ImSettings &settings, ImResponse &response)
202 DrawAssetSlot(label, value, vex::GetEditorIcon_Internal(
"audio"),
"Audio");
205 inline void tag_invoke(ImReflect::ImInput_t,
const char *label,
vex::asset_path &value, ImSettings &settings, ImResponse &response)
207 DrawAssetSlot(label, value, vex::GetEditorIcon_Internal(
"file"),
"Asset");
210 inline void tag_invoke(ImReflect::ImInput_t,
const char* label, glm::vec4& value, ImSettings& settings, ImResponse& response) {
211 ImGui::DragFloat4(label, &value.x);
214 inline void tag_invoke(ImReflect::ImInput_t,
const char* label, glm::vec3& value, ImSettings& settings, ImResponse& response) {
215 ImGui::DragFloat3(label, &value.x);
218 inline void tag_invoke(ImReflect::ImInput_t,
const char* label, glm::vec2& value, ImSettings& settings, ImResponse& response) {
219 ImGui::DragFloat2(label, &value.x);
222 inline void tag_invoke(ImReflect::ImInput_t,
const char* label,
vex::rgb& value, ImSettings& settings, ImResponse& response) {
223 ImGui::ColorEdit3(label, &value.x);
226 inline void tag_invoke(ImReflect::ImInput_t,
const char* label,
vex::rgba& value, ImSettings& settings, ImResponse& response) {
227 ImGui::ColorEdit4(label, &value.x);
232 IMGUI_REFLECT(glm::vec2, x, y);
233 IMGUI_REFLECT(glm::vec3, x, y, z);
234 IMGUI_REFLECT(glm::vec4, x, y, z, w);
237 inline void tag_invoke(ImReflect::ImInput_t,
const char* label, glm::quat& value, ImSettings& settings, ImResponse& response) {
238 glm::vec3 euler = glm::degrees(glm::eulerAngles(value));
239 if (ImGui::DragFloat3(label, &euler.x)) {
240 value = glm::quat(glm::radians(euler));
244 inline void tag_invoke(ImReflect::ImInput_t,
const char* label, glm::ivec2& value, ImSettings& settings, ImResponse& response) {
245 ImGui::DragInt2(label, &value.x);
247 inline void tag_invoke(ImReflect::ImInput_t,
const char* label, glm::ivec3& value, ImSettings& settings, ImResponse& response) {
248 ImGui::DragInt3(label, &value.x);
251 IMGUI_REFLECT(glm::quat, x, y, z, w);
252 IMGUI_REFLECT(glm::ivec2, x, y);
253 IMGUI_REFLECT(glm::ivec3, x, y, z);
255 #define IMGUI_REFLECT(...);
261 void GenericComponentInspector(
GameObject& obj) {
263 if (obj.HasComponent<T>()) {
264 std::string name =
typeid(T).name();
265 size_t lastColon = name.rfind(
"::");
266 size_t lastBracket = name.find(
']');
267 std::string extracted = (lastColon != std::string::npos)
268 ? name.substr(lastColon + 2, (lastBracket != std::string::npos ? lastBracket : name.length()) - (lastColon + 2))
271 ImGui::PushID(name.c_str());
272 if (ImGui::CollapsingHeader(extracted.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
273 auto& component = obj.GetComponent<T>();
275 ImReflect::Input(
"##data", component);
277 ImGui::Dummy(ImVec2(5.0f, 5.0f));
279 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.47f, 0.05f, 0.05f, 1.0f));
280 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.71f, 0.10f, 0.10f, 1.0f));
281 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.30f, 0.03f, 0.03f, 1.0f));
282 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.94f, 0.85f, 0.85f, 1.0f));
284 if (ImGui::Button(
"Remove")) {
285 obj.GetEngine().getRegistry().remove<T>(obj.GetEntity());
288 ImGui::PopStyleColor(4);
298class ComponentRegistry {
300 using ComponentLoader = std::function<void(
GameObject&,
const nlohmann::json&)>;
301 using ComponentSaver = std::function<nlohmann::json(
GameObject&)>;
302 using ComponentInspector = std::function<void(
GameObject&)>;
303 using ComponentChecker = std::function<bool(
const GameObject&)>;
304 using ComponentCreator = std::function<void(
GameObject&)>;
306 static ComponentRegistry& getInstance();
319 loaders[name] = [](
GameObject& obj,
const nlohmann::json& j) {
321 if constexpr (std::is_default_constructible_v<T>) {
331 savers[name] = [](
GameObject& obj) -> nlohmann::json {
338 inspectors[name] = &GenericComponentInspector<T>;
346 if constexpr (std::is_default_constructible_v<T>) {
349 log(
"Error: Component '%s' is not default constructible.",
typeid(T).name());
354 registeredNames.push_back(name);
357 dynamicComponents.push_back(name);
399 const std::unordered_map<std::string, ComponentInspector>&
getInspectors()
const;
405 void SetEditorIcon(
const std::string &name, ImTextureID icon);
406 ImTextureID GetEditorIcon(
const std::string &name);
411 std::vector<std::string> registeredNames;
412 std::vector<std::string> dynamicComponents;
413 std::unordered_map<std::string, ComponentLoader> loaders;
414 std::unordered_map<std::string, ComponentSaver> savers;
415 std::unordered_map<std::string, ComponentInspector> inspectors;
416 std::unordered_map<std::string, ComponentChecker> checkers;
417 std::unordered_map<std::string, ComponentCreator> creators;
419 std::unordered_map<std::string, ImTextureID> m_editorIcons;
426#define VEX_CAT_IMPL(a, b) a##b
427#define VEX_CAT(a, b) VEX_CAT_IMPL(a, b)
428#define VEX_UNIQUE_NAME(prefix) VEX_CAT(prefix, __LINE__)
430#ifdef GAME_MODULE_EXPORTS
436 #define REGISTER_COMPONENT(Type, ...) \
437 namespace vex { NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, __VA_ARGS__) } \
438 IMGUI_REFLECT(Type, __VA_ARGS__) \
440 struct VEX_UNIQUE_NAME(Registrar_) { \
441 VEX_UNIQUE_NAME(Registrar_)() { \
442 vex::ComponentRegistry::getInstance().registerComponent<Type>(#Type, true); \
445 static VEX_UNIQUE_NAME(Registrar_) VEX_UNIQUE_NAME(g_registrar_); \
449 #define REGISTER_COMPONENT_CUSTOM(Type, ...) \
450 IMGUI_REFLECT(Type, __VA_ARGS__) \
452 struct VEX_UNIQUE_NAME(Registrar_) { \
453 VEX_UNIQUE_NAME(Registrar_)() { \
454 vex::ComponentRegistry::getInstance().registerComponent<Type>(#Type, true); \
457 static VEX_UNIQUE_NAME(Registrar_) VEX_UNIQUE_NAME(g_registrar_); \
466 #define REGISTER_COMPONENT(Type, ...) \
467 namespace vex { NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, __VA_ARGS__) } \
468 IMGUI_REFLECT(Type, __VA_ARGS__) \
470 struct VEX_UNIQUE_NAME(Registrar_) { \
471 VEX_UNIQUE_NAME(Registrar_)() { \
472 vex::ComponentRegistry::getInstance().registerComponent<Type>(#Type, false); \
474 ~VEX_UNIQUE_NAME(Registrar_)() { \
475 vex::ComponentRegistry::getInstance().unregisterComponent(#Type); \
478 static VEX_UNIQUE_NAME(Registrar_) VEX_UNIQUE_NAME(g_registrar_); \
482 #define REGISTER_COMPONENT_CUSTOM(Type, ...) \
483 IMGUI_REFLECT(Type, __VA_ARGS__) \
485 struct VEX_UNIQUE_NAME(Registrar_) { \
486 VEX_UNIQUE_NAME(Registrar_)() { \
487 vex::ComponentRegistry::getInstance().registerComponent<Type>(#Type, false); \
489 ~VEX_UNIQUE_NAME(Registrar_)() { \
490 vex::ComponentRegistry::getInstance().unregisterComponent(#Type); \
493 static VEX_UNIQUE_NAME(Registrar_) VEX_UNIQUE_NAME(g_registrar_); \
Contains basic components like transform, camera, name components..
This file defines the GameObject class.
This file defines MeshData struct and all other structs needed for it.
Allows for imgui and json serialization of components, for easy loading, saving and drawing them.
Class registering GameComponents mainly used to load them in SceneManager.
nlohmann::json saveComponent(GameObject &obj, const std::string &type)
Serializes a component on a GameObject to JSON.
const std::unordered_map< std::string, ComponentInspector > & getInspectors() const
Get component inspectors.
void registerComponent(const std::string &name, bool isDynamic=false)
Template function to register a component type with the system.
void clearDynamicComponents()
Clears all components registered as dynamic.
bool loadComponent(GameObject &obj, const std::string &type, const nlohmann::json &json)
Loads data into a component on a GameObject from JSON.
void createComponent(GameObject &obj, const std::string &name)
Creates and attaches a specific component to a game object.
std::vector< std::string > getMissingComponents(const GameObject &obj)
Get missing components for a game object.
const std::vector< std::string > & getRegisteredNames() const
Get registered component names.
void unregisterComponent(const std::string &name)
Unregisters a component type and removes all associated callbacks.
void drawInspectorForObject(GameObject &obj)
Draws the ImGui inspector for all components on the object.
Its base class for all game objects, eg. Player, Enemy, Weapon. Your Class needs to inherit from it.
bool HasComponent() const
Function that checks if the GameObject has a component.
T & GetComponent()
Function that returns reference to a requested component.
void AddComponent(const T &comp)
Function that adds a component to the GameObject.
std::string VEX_EXPORT GetAssetDir()
Gets the current asset directory override.
void VEX_EXPORT log(const char *fmt,...)
Logs a formatted message.
std::string VEX_EXPORT GetAssetPath(const std::string &relativePath)
Resolves the absolute path of an asset based on the current build configuration.
Mesh data structure for loading and managing mesh data.
Base wrapper for generic assets. Currently doesnt add any functionality. Its used mainly for custom f...
Specific wrapper for Audio.
Specific wrapper for Meshes.
Represents an RGB color value. Used mainly for fancy rendering in editor.
Represents an RGBA color value. Used mainly for fancy rendering in editor.
Specific wrapper for Textures.