From fbb0574b81a786bc72922e9d26aebca857d96393 Mon Sep 17 00:00:00 2001 From: OusmBlueNinja <89956790+OusmBlueNinja@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:56:45 -0500 Subject: [PATCH] working on tilemap --- imgui.ini | 23 +- remake/build.log | 5 +- src/assets/scenes/TilemapTest.cene | 52 ++++ src/src/Components/Component.h | 2 + src/src/Components/TilemapComponent.cpp | 322 +++++++----------------- src/src/Components/TilemapComponent.h | 69 +++-- src/src/Engine.cpp | 62 ++++- src/src/Engine.h | 2 + src/src/Entitys/Object.cpp | 1 + src/src/Renderer.cpp | 64 ++--- src/src/Renderer.h | 3 +- src/src/core/utils/LoadingWindow.cpp | 101 ++++++++ src/src/core/utils/LoadingWindow.h | 19 ++ src/src/core/utils/Texture.h | 2 +- src/src/core/utils/TextureAtlas.h | 9 +- src/src/editor/windows/Inspector.cpp | 79 ++++-- 16 files changed, 459 insertions(+), 356 deletions(-) create mode 100644 src/assets/scenes/TilemapTest.cene create mode 100644 src/src/core/utils/LoadingWindow.cpp create mode 100644 src/src/core/utils/LoadingWindow.h diff --git a/imgui.ini b/imgui.ini index f95f81e..c006b8d 100644 --- a/imgui.ini +++ b/imgui.ini @@ -14,8 +14,8 @@ Size=1280,701 Collapsed=0 [Window][Inspector] -Pos=938,19 -Size=342,701 +Pos=900,19 +Size=380,701 Collapsed=0 DockId=0x00000006,0 @@ -27,23 +27,24 @@ DockId=0x00000003,0 [Window][Viewport] Pos=344,19 -Size=592,390 +Size=554,390 Collapsed=0 DockId=0x00000007,0 [Window][##MainMenuBar] -Size=1920,19 +Pos=0,0 +Size=1280,19 Collapsed=0 [Window][Performance Info] Pos=344,411 -Size=592,309 +Size=554,309 Collapsed=0 DockId=0x00000008,3 [Window][Console] Pos=344,411 -Size=592,309 +Size=554,309 Collapsed=0 DockId=0x00000008,0 @@ -55,7 +56,7 @@ DockId=0x00000007,1 [Window][Profiler] Pos=344,411 -Size=592,309 +Size=554,309 Collapsed=0 DockId=0x00000008,2 @@ -79,7 +80,7 @@ DockId=0x00000008,1 [Window][Color Correction] Pos=344,411 -Size=592,309 +Size=554,309 Collapsed=0 DockId=0x00000008,1 @@ -91,12 +92,12 @@ DockId=0x00000004,0 [Docking][Data] DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X - DockNode ID=0x00000005 Parent=0x11111111 SizeRef=1576,1158 Split=X + DockNode ID=0x00000005 Parent=0x11111111 SizeRef=1538,1158 Split=X DockNode ID=0x00000001 Parent=0x00000005 SizeRef=342,701 Split=Y Selected=0x12EF0F59 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=342,575 HiddenTabBar=1 Selected=0x12EF0F59 DockNode ID=0x00000004 Parent=0x00000001 SizeRef=342,581 HiddenTabBar=1 Selected=0x36AF052B - DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1576,701 Split=Y Selected=0xC450F867 + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1194,701 Split=Y Selected=0xC450F867 DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,847 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867 DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,309 Selected=0x9B5D3198 - DockNode ID=0x00000006 Parent=0x11111111 SizeRef=342,1158 HiddenTabBar=1 Selected=0x36DC96AB + DockNode ID=0x00000006 Parent=0x11111111 SizeRef=380,1158 HiddenTabBar=1 Selected=0x36DC96AB diff --git a/remake/build.log b/remake/build.log index ee9679b..0394e77 100644 --- a/remake/build.log +++ b/remake/build.log @@ -1,2 +1,3 @@ -[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\AnimationComponent.o src\build\Components\CameraComponent.o src\build\Components\LightComponent.o src\build\Components\ParticleComponent.o src\build\Components\PhysicsComponent.o src\build\Components\ScriptComponent.o src\build\Components\SpriteComponent.o src\build\Components\TextComonent.o src\build\Components\TilemapComponent.o src\build\core\utils\AssetManager.o src\build\core\utils\EngineConfig.o src\build\core\utils\ExceptionHandler.o src\build\core\utils\FileDialog.o src\build\core\utils\input.o src\build\core\utils\Logging.o src\build\core\utils\Profiler.o src\build\core\utils\Texture.o src\build\core\utils\utils.o src\build\editor\windows\AssetBrowser.o src\build\editor\windows\Inspector.o src\build\Entitys\Object.o src\build\utils\GameObjectsList.o src\build\utils\Shader.o src\build\utils\UID.o src\build\lapi.o src\build\lauxlib.o src\build\lbaselib.o src\build\lcode.o src\build\lcorolib.o src\build\lctype.o src\build\ldblib.o src\build\ldebug.o src\build\ldo.o src\build\ldump.o src\build\lfunc.o src\build\lgc.o src\build\linit.o src\build\liolib.o src\build\llex.o src\build\lmathlib.o src\build\lmem.o src\build\loadlib.o src\build\lobject.o src\build\lopcodes.o src\build\loslib.o src\build\lparser.o src\build\lstate.o src\build\lstring.o src\build\lstrlib.o src\build\ltable.o src\build\ltablib.o src\build\ltm.o src\build\lua.o src\build\luac.o src\build\lundump.o src\build\lutf8lib.o src\build\lvm.o src\build\lzio.o src\build\imgui.o src\build\imgui_demo.o src\build\imgui_draw.o src\build\imgui_impl_glfw.o src\build\imgui_impl_opengl3.o src\build\imgui_tables.o src\build\imgui_widgets.o src\build\aabb.o src\build\arena_allocator.o src\build\array.o src\build\bitset.o src\build\body.o src\build\broad_phase.o src\build\constraint_graph.o src\build\contact.o src\build\contact_solver.o src\build\core.o src\build\distance.o src\build\distance_joint.o src\build\dynamic_tree.o src\build\geometry.o src\build\hull.o src\build\id_pool.o src\build\island.o src\build\joint.o src\build\manifold.o src\build\math_functions.o src\build\motor_joint.o src\build\mouse_joint.o src\build\mover.o src\build\prismatic_joint.o src\build\revolute_joint.o src\build\sensor.o src\build\shape.o src\build\solver.o src\build\solver_set.o src\build\table.o src\build\timer.o src\build\types.o src\build\weld_joint.o src\build\wheel_joint.o src\build\world.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto -[RUN] Executed app.exe successfully. +[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\AnimationComponent.o src\build\Components\CameraComponent.o src\build\Components\LightComponent.o src\build\Components\ParticleComponent.o src\build\Components\PhysicsComponent.o src\build\Components\ScriptComponent.o src\build\Components\SpriteComponent.o src\build\Components\TextComonent.o src\build\Components\TilemapComponent.o src\build\core\utils\AssetManager.o src\build\core\utils\EngineConfig.o src\build\core\utils\ExceptionHandler.o src\build\core\utils\FileDialog.o src\build\core\utils\input.o src\build\core\utils\LoadingWindow.o src\build\core\utils\Logging.o src\build\core\utils\Profiler.o src\build\core\utils\Texture.o src\build\core\utils\utils.o src\build\editor\windows\AssetBrowser.o src\build\editor\windows\Inspector.o src\build\Entitys\Object.o src\build\utils\GameObjectsList.o src\build\utils\Shader.o src\build\utils\UID.o src\build\lapi.o src\build\lauxlib.o src\build\lbaselib.o src\build\lcode.o src\build\lcorolib.o src\build\lctype.o src\build\ldblib.o src\build\ldebug.o src\build\ldo.o src\build\ldump.o src\build\lfunc.o src\build\lgc.o src\build\linit.o src\build\liolib.o src\build\llex.o src\build\lmathlib.o src\build\lmem.o src\build\loadlib.o src\build\lobject.o src\build\lopcodes.o src\build\loslib.o src\build\lparser.o src\build\lstate.o src\build\lstring.o src\build\lstrlib.o src\build\ltable.o src\build\ltablib.o src\build\ltm.o src\build\lua.o src\build\luac.o src\build\lundump.o src\build\lutf8lib.o src\build\lvm.o src\build\lzio.o src\build\imgui.o src\build\imgui_demo.o src\build\imgui_draw.o src\build\imgui_impl_glfw.o src\build\imgui_impl_opengl3.o src\build\imgui_tables.o src\build\imgui_widgets.o src\build\aabb.o src\build\arena_allocator.o src\build\array.o src\build\bitset.o src\build\body.o src\build\broad_phase.o src\build\constraint_graph.o src\build\contact.o src\build\contact_solver.o src\build\core.o src\build\distance.o src\build\distance_joint.o src\build\dynamic_tree.o src\build\geometry.o src\build\hull.o src\build\id_pool.o src\build\island.o src\build\joint.o src\build\manifold.o src\build\math_functions.o src\build\motor_joint.o src\build\mouse_joint.o src\build\mover.o src\build\prismatic_joint.o src\build\revolute_joint.o src\build\sensor.o src\build\shape.o src\build\solver.o src\build\solver_set.o src\build\table.o src\build\timer.o src\build\types.o src\build\weld_joint.o src\build\wheel_joint.o src\build\world.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto +[ERROR] Runtime crash +Command 'src\build\app.exe' returned non-zero exit status 1. diff --git a/src/assets/scenes/TilemapTest.cene b/src/assets/scenes/TilemapTest.cene new file mode 100644 index 0000000..f992dff --- /dev/null +++ b/src/assets/scenes/TilemapTest.cene @@ -0,0 +1,52 @@ +engine_version: 0.1.0 +scene_name: New Text Document +scene_hash: ab18a816340bea7fd8b460cde813cb5277bc2a67ca413862f648cf030c93a85f +format_version: 1 +objects: + - name: TilemapTest + uid: 4afa4d0c474b4fb9b9b80eb1bb31195a + id: 0 + position: [0, 0] + rotation: 0 + layer: 0 + visable: true + components: + - Type: TilemapComponent + TextureUAID: 1 + TexelWidth: 1 + TexelHeight: 1 + TileSize: + x: 32 + y: 32 + Tiles: + - [0, 0, 1] + - [1, 0, 2] + - [2, 0, 3] + - [0, 1, 4] + - [1, 1, 5] + - [2, 1, 6] + - [0, 2, 7] + - [1, 2, 8] + - [2, 2, 9] + - [3, 3, 10] + - [4, 3, 11] + - [5, 3, 12] + - [6, 3, 13] + - [6, 2, 14] + - [6, 1, 15] + - [6, 0, 16] + children: [] +color_correction: + brightness: 1 + saturation: 1 + gamma: 1 + bloom: true + intensity: 1.2 + threshold: 1 +Assets: + - uaid: 1 + path: C:\Users\spenc\OneDrive\Pictures\6656e7221e49a1774d2fb280357e56f8d25d9d95.png + filename: 6656e7221e49a1774d2fb280357e56f8d25d9d95.png + filetype: png + type: 0 + size: [1024, 1024] diff --git a/src/src/Components/Component.h b/src/src/Components/Component.h index 3449851..7b4f0fe 100644 --- a/src/src/Components/Component.h +++ b/src/src/Components/Component.h @@ -5,6 +5,8 @@ #include #include #include "../core/utils/ExceptionHandler.h" +#include "../core/utils/AssetManager.h" +#include "../core/utils/Logging.h" class Object; diff --git a/src/src/Components/TilemapComponent.cpp b/src/src/Components/TilemapComponent.cpp index 085c2ea..70d57cf 100644 --- a/src/src/Components/TilemapComponent.cpp +++ b/src/src/Components/TilemapComponent.cpp @@ -1,262 +1,126 @@ #include "TilemapComponent.h" -#include "../core/utils/Logging.h" -#include "../core/utils/FileDialog.h" -#include "../core/utils/utils.h" -#include -#include -#include -#include - -static int g_selectedTileIndex = 0; +#include "../Renderer.h" -// Constructor: initialize with a default grid (optional) -TilemapComponent::TilemapComponent(Object *owner) +TilemapComponent::TilemapComponent(Object* owner) : Component(owner) { - // For example, create an empty 10x10 tilemap - SetGridSize(glm::ivec2(10, 10)); - // Optionally set a default tile size - m_TileSize = glm::ivec2(32, 32); + renderType = RenderType::Unlit; } -// Set grid dimensions and resize tile array; initially all -1 (empty) -void TilemapComponent::SetGridSize(const glm::ivec2 &size) +uint64_t TilemapComponent::PackCoord(int x, int y) { - m_GridSize = size; - m_Tiles.assign(size.x * size.y, -1); + return (static_cast(static_cast(x)) << 32) | + static_cast(y); } -// Set individual tile at (x, y) -void TilemapComponent::SetTile(int x, int y, int tileIndex) +void TilemapComponent::UnpackCoord(uint64_t key, int& x, int& y) { - if (x < 0 || y < 0 || x >= m_GridSize.x || y >= m_GridSize.y) + x = static_cast(key >> 32); + y = static_cast(key & 0xFFFFFFFF); +} + +void TilemapComponent::SetTextureAtlas(uint64_t uaid, int texelWidth, int texelHeight) +{ + textureUAID = uaid; + + const auto* asset = AssetManager::GetAssetByID(uaid); + if (!asset || !asset->loaded) + { + Logger::LogError("TilemapComponent: Asset not found or not loaded (UAID: %llu)", uaid); return; - m_Tiles[y * m_GridSize.x + x] = tileIndex; + } + + texture = std::make_shared(uaid); + + atlas.texture = texture; + atlas.SetTexelSize(texelWidth, texelHeight); + + int texWidth = static_cast(asset->size.x); + int texHeight = static_cast(asset->size.y); + + atlasColumns = texWidth / texelWidth; + atlasRows = texHeight / texelHeight; +} + +void TilemapComponent::SetTile(int x, int y, int index) +{ + uint64_t key = PackCoord(x, y); + if (index >= 0) + tiles[key] = index; + else + tiles.erase(key); } int TilemapComponent::GetTile(int x, int y) const { - if (x < 0 || y < 0 || x >= m_GridSize.x || y >= m_GridSize.y) - return -1; - return m_Tiles[y * m_GridSize.x + x]; -} - -void TilemapComponent::SetTileSize(const glm::ivec2 &size) -{ - m_TileSize = size; -} - -// Set atlas (tileset) settings; also update dimensions -void TilemapComponent::SetAtlas(const std::string &path, int atlasTileWidth, int atlasTileHeight) -{ - m_AtlasPath = path; - m_AtlasTileWidth = atlasTileWidth; - m_AtlasTileHeight = atlasTileHeight; - UpdateAtlasDimensions(); -} - -// Helper: load atlas image info using stbi_info to compute columns and rows -void TilemapComponent::UpdateAtlasDimensions() -{ - if (m_AtlasPath.empty()) - return; - int texW, texH, comp; - if (stbi_info(m_AtlasPath.c_str(), &texW, &texH, &comp)) - { - m_AtlasCols = texW / m_AtlasTileWidth; - m_AtlasRows = texH / m_AtlasTileHeight; - } - else - { - m_AtlasCols = m_AtlasRows = 0; - Logger::LogError("Failed to get atlas info: %s", m_AtlasPath.c_str()); - } -} - - -bool TilemapComponent::IsTileFullyTransparent(int col, int row, int texW, int texH, unsigned char* imageData) -{ - int x0 = col * m_AtlasTileWidth; - int y0 = row * m_AtlasTileHeight; - - for (int y = 0; y < m_AtlasTileHeight; ++y) - { - for (int x = 0; x < m_AtlasTileWidth; ++x) - { - int px = x0 + x; - int py = y0 + y; - int idx = (py * texW + px) * 4; - if (imageData[idx + 3] > 0) - return false; - } - } - return true; + uint64_t key = PackCoord(x, y); + auto it = tiles.find(key); + return (it != tiles.end()) ? it->second : -1; } -// Serialization: store grid size, tile size, tile data, and atlas settings. -void TilemapComponent::Save(YAML::Emitter &out) const +// Accessors +uint64_t TilemapComponent::GetTextureUAID() const { return textureUAID; } +std::shared_ptr TilemapComponent::GetTexture() const { return texture; } +const TextureAtlas& TilemapComponent::GetAtlas() const { return atlas; } +const core::types::Vec2& TilemapComponent::GetTileSize() const { return tileSize; } +void TilemapComponent::SetTileSize(const core::types::Vec2& size) { tileSize = size; } +int TilemapComponent::GetAtlasColumns() const { return atlasColumns; } +int TilemapComponent::GetAtlasRows() const { return atlasRows; } +const std::unordered_map& TilemapComponent::GetTileData() const { return tiles; } + +// Save to YAML +void TilemapComponent::Save(YAML::Emitter& out) const { out << YAML::BeginMap; - out << YAML::Key << "type" << YAML::Value << "TilemapComponent"; - out << YAML::Key << "gridSize" << YAML::Value << std::vector{m_GridSize.x, m_GridSize.y}; - out << YAML::Key << "tileSize" << YAML::Value << std::vector{m_TileSize.x, m_TileSize.y}; - out << YAML::Key << "tiles" << YAML::Value << m_Tiles; - out << YAML::Key << "atlasPath" << YAML::Value << m_AtlasPath; - out << YAML::Key << "atlasTileSize" << YAML::Value << std::vector{m_AtlasTileWidth, m_AtlasTileHeight}; + out << YAML::Key << "Type" << YAML::Value << GetName(); + out << YAML::Key << "TextureUAID" << YAML::Value << textureUAID; + out << YAML::Key << "TexelWidth" << YAML::Value << atlas.GetTexelSize().x; + out << YAML::Key << "TexelHeight" << YAML::Value << atlas.GetTexelSize().y; + + out << YAML::Key << "TileSize" << YAML::Value << YAML::BeginMap; + out << YAML::Key << "x" << YAML::Value << tileSize.x; + out << YAML::Key << "y" << YAML::Value << tileSize.y; + out << YAML::EndMap; + + out << YAML::Key << "Tiles" << YAML::Value << YAML::BeginSeq; + for (const auto& [key, index] : tiles) + { + int x, y; + UnpackCoord(key, x, y); + out << YAML::Flow << YAML::BeginSeq << x << y << index << YAML::EndSeq; + } + out << YAML::EndSeq; out << YAML::EndMap; } -// Deserialization: load saved data -void TilemapComponent::Load(const YAML::Node &node) + + +void TilemapComponent::Load(const YAML::Node& node) { - if (node["gridSize"]) + textureUAID = node["TextureUAID"].as(); + int texelWidth = node["TexelWidth"].as(); + int texelHeight = node["TexelHeight"].as(); + SetTextureAtlas(textureUAID, texelWidth, texelHeight); + + if (node["TileSize"]) { - auto vec = node["gridSize"].as>(); - if (vec.size() == 2) - m_GridSize = glm::ivec2(vec[0], vec[1]); + tileSize.x = node["TileSize"]["x"].as(); + tileSize.y = node["TileSize"]["y"].as(); } - if (node["tileSize"]) + + tiles.clear(); + if (node["Tiles"]) { - auto vec = node["tileSize"].as>(); - if (vec.size() == 2) - m_TileSize = glm::ivec2(vec[0], vec[1]); - } - if (node["tiles"]) - m_Tiles = node["tiles"].as>(); - if (node["atlasPath"]) - m_AtlasPath = node["atlasPath"].as(); - if (node["atlasTileSize"]) - { - auto vec = node["atlasTileSize"].as>(); - if (vec.size() == 2) + for (const auto& tile : node["Tiles"]) { - m_AtlasTileWidth = vec[0]; - m_AtlasTileHeight = vec[1]; + if (!tile.IsSequence() || tile.size() != 3) continue; + int x = tile[0].as(); + int y = tile[1].as(); + int index = tile[2].as(); + SetTile(x, y, index); } } - // Update atlas dimensions after loading settings - UpdateAtlasDimensions(); } - -void TilemapComponent::DrawEditorUI() -{ - ImGui::Begin("Tilemap Editor"); - - if (ImGui::BeginTabBar("TilemapEditorTabs")) - { - // --- Atlas Settings --- - if (ImGui::BeginTabItem("Atlas Settings")) - { - ImGui::Text("Current Atlas: %s", m_AtlasPath.empty() ? "None" : m_AtlasPath.c_str()); - if (ImGui::Button("Import Atlas")) - { - std::string path = OpenFileDialog(FileDialogType::Images); - if (!path.empty()) - { - m_AtlasPath = path; - UpdateAtlasDimensions(); - } - } - - int atlasW = m_AtlasTileWidth, atlasH = m_AtlasTileHeight; - if (ImGui::InputInt("Atlas Tile Width", &atlasW)) - { - m_AtlasTileWidth = atlasW; - UpdateAtlasDimensions(); - } - if (ImGui::InputInt("Atlas Tile Height", &atlasH)) - { - m_AtlasTileHeight = atlasH; - UpdateAtlasDimensions(); - } - - ImGui::Text("Atlas Grid: %d x %d", m_AtlasCols, m_AtlasRows); - ImGui::EndTabItem(); - } - - // --- Map Settings --- - if (ImGui::BeginTabItem("Map Settings")) - { - ImGui::Text("Map Size: %d x %d", m_GridSize.x, m_GridSize.y); - ImGui::Text("Tile Size: %d x %d", m_TileSize.x, m_TileSize.y); - ImGui::Text("Tile Count: %zu", m_Tiles.size()); - - - ImGui::EndTabItem(); - } - - // --- Tile Tools --- - if (ImGui::BeginTabItem("Tiles")) - { - - ImGui::Separator(); - - if (m_AtlasPath.empty()) - { - ImGui::Text("No atlas loaded."); - } - else - { - int texW, texH, comp; - unsigned char* imageData = stbi_load(m_AtlasPath.c_str(), &texW, &texH, &comp, 4); - if (imageData) - { - float uvTileW = float(m_AtlasTileWidth) / float(texW); - float uvTileH = float(m_AtlasTileHeight) / float(texH); - GLuint textureID = LoadTextureIfNeeded(m_AtlasPath); - - const float tileDisplaySize = 48; - ImGui::BeginChild("TilesetView"); - for (int row = 0; row < m_AtlasRows; ++row) - { - for (int col = 0; col < m_AtlasCols; ++col) - { - int tileIndex = row * m_AtlasCols + col; - float uvX = float(col) * uvTileW; - float uvY = float(row) * uvTileH; - ImVec2 uv0(uvX, uvY); - ImVec2 uv1(uvX + uvTileW, uvY + uvTileH); - - char id[32]; - sprintf(id, "##tile_%d", tileIndex); - - bool isEmpty = IsTileFullyTransparent(col, row, texW, texH, imageData); - ImVec4 tint = isEmpty ? ImVec4(0.3f, 0.3f, 0.3f, 0.5f) : ImVec4(1, 1, 1, 1); - if (ImGui::ImageButton(id, (ImTextureID)(uintptr_t)textureID, ImVec2(tileDisplaySize, tileDisplaySize), uv0, uv1)) - { - g_selectedTileIndex = tileIndex; - } - - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Tile %d", tileIndex); - - ImGui::SameLine(); - } - ImGui::NewLine(); - } - stbi_image_free(imageData); - ImGui::EndChild(); - } - else - { - ImGui::Text("Failed to load image."); - } - - ImGui::Text("Selected Tile: %d", g_selectedTileIndex); - } - - ImGui::EndTabItem(); - } - - ImGui::EndTabBar(); - } - - ImGui::End(); -} - - - - diff --git a/src/src/Components/TilemapComponent.h b/src/src/Components/TilemapComponent.h index cdce557..984d107 100644 --- a/src/src/Components/TilemapComponent.h +++ b/src/src/Components/TilemapComponent.h @@ -1,58 +1,47 @@ #pragma once #include "Component.h" -#include -#include -#include -#include -#include +#include "../core/utils/TextureAtlas.h" +#include "../core/types/Vec2.h" +#include +#include class TilemapComponent : public Component { public: TilemapComponent(Object* owner); - // Editor UI for tilemap - void DrawEditorUI(); - - // Set up grid and tile dimensions - void SetGridSize(const glm::ivec2& size); - void SetTileSize(const glm::ivec2& size); - void SetTile(int x, int y, int tileIndex); + void SetTextureAtlas(uint64_t uaid, int texelWidth, int texelHeight); + void SetTile(int x, int y, int index); int GetTile(int x, int y) const; - - // Getters - const glm::ivec2& GetGridSize() const { return m_GridSize; } - const glm::ivec2& GetTileSize() const { return m_TileSize; } - const std::vector& GetTiles() const { return m_Tiles; } - const std::string& GetAtlasPath() const { return m_AtlasPath; } - - int GetAtlasTileWidth() const { return m_AtlasTileWidth; } - int GetAtlasTileHeight() const { return m_AtlasTileHeight; } - int GetAtlasCols() const { return m_AtlasCols; } - int GetAtlasRows() const { return m_AtlasRows; } - // Atlas setup: import an atlas image and configure its tile dimensions. - void SetAtlas(const std::string& path, int atlasTileWidth, int atlasTileHeight); - // Serialization + // Overrides + std::string GetName() const override { return "TilemapComponent"; } void Save(YAML::Emitter& out) const override; void Load(const YAML::Node& node) override; - std::string GetName() const override { return "TilemapComponent"; } + + // Accessors + uint64_t GetTextureUAID() const; + std::shared_ptr GetTexture() const; + const TextureAtlas& GetAtlas() const; + const core::types::Vec2& GetTileSize() const; + void SetTileSize(const core::types::Vec2& size); + int GetAtlasColumns() const; + int GetAtlasRows() const; + const std::unordered_map& GetTileData() const; + + static uint64_t PackCoord(int x, int y); + static void UnpackCoord(uint64_t key, int& x, int& y); private: - // Grid and tile data - glm::ivec2 m_GridSize {0, 0}; - glm::ivec2 m_TileSize {32, 32}; - std::vector m_Tiles; + uint64_t textureUAID = 0; + std::shared_ptr texture; + TextureAtlas atlas; + RenderType renderType; - // Atlas (tileset) settings - std::string m_AtlasPath; - int m_AtlasTileWidth = 32; - int m_AtlasTileHeight = 32; - int m_AtlasCols = 0; - int m_AtlasRows = 0; - - void UpdateAtlasDimensions(); - bool IsTileFullyTransparent(int col, int row, int texW, int texH, unsigned char* imageData); + core::types::Vec2 tileSize = {32, 32}; + int atlasColumns = 0; + int atlasRows = 0; + std::unordered_map tiles; }; diff --git a/src/src/Engine.cpp b/src/src/Engine.cpp index 2b2a0a3..17840ec 100644 --- a/src/src/Engine.cpp +++ b/src/src/Engine.cpp @@ -17,6 +17,7 @@ #include "utils/GameObjectsList.h" #include "core/utils/Profiler.h" #include "core/utils/utils.h" +#include "core/utils/LoadingWindow.h" #include "editor/windows/AssetBrowser.h" #include "editor/windows/Inspector.h" @@ -513,12 +514,15 @@ void Engine::Init() Logger::LogVerbose("Resverving Objects"); - m_toDraw.reserve(1024); - m_scriptUpdates.reserve(10); - m_collectStack.reserve(1024); - m_physicsUpdates.reserve(1024); - m_particleUpdates.reserve(100); - m_animationsUpdates.reserve(100); + + // These values were AI Generated. + + m_toDraw.reserve(2048); // ~2K sprites per frame (including UI, particles, objects) + m_scriptUpdates.reserve(256); // ~256 active script components per frame + m_collectStack.reserve(2048); // Used for internal temp memory, 512 is safe unless deep recursion + m_physicsUpdates.reserve(512); // 256-512 physics bodies/entities with Box2D + m_particleUpdates.reserve(24); // ~1K particle systems active is fair in effects-heavy scenes + m_animationsUpdates.reserve(512); // ~500 animated objects (characters, FX, etc.) Logger::LogInfo("Initialized Engine"); } @@ -572,6 +576,7 @@ void Engine::collectObjects(bool playing, const glm::vec2 &camPos, float camZoom if (auto animator = obj->GetComponent()) m_animationsUpdates.push_back(animator.get()); + if (playing) { if (auto script = obj->GetComponent()) @@ -914,6 +919,13 @@ void Engine::Run() profiler.EndSection(); } + if (auto tilemap = obj->GetComponent()) + { + profiler.BeginSection("Draw Tilemap: " + obj->GetName()); + Renderer::DrawTilemap(tilemap.get(), worldPos, cameraZoom, cameraPos); + profiler.EndSection(); + } + if (auto animator = obj->GetComponent()) { profiler.BeginSection("Draw Animator: " + obj->GetName()); @@ -1159,17 +1171,24 @@ void Engine::LoadScene(const std::string &path) Logger::LogDebug("[LoadScene] Reading Scene File."); YAML::Node root = YAML::LoadFile(path); - Logger::LogDebug("[LoadScene] Verifying Scene"); + LoadingWindow loadingUI; + loadingUI.Create("Loading Scene"); + + std::string currentStep = "Verifying Scene"; + std::string currentDetail = ""; + loadingUI.Update(currentStep, currentDetail, 0.0f); if (!root["engine_version"] || !root["format_version"] || !root["scene_name"]) { Logger::LogError("[LoadScene] Missing required metadata!"); + loadingUI.Destroy(); return; } if (root["engine_version"].as() != "0.1.0") { Logger::LogError("[LoadScene] Version mismatch! Expected 0.1.0, got %s", root["engine_version"].as().c_str()); + loadingUI.Destroy(); return; } @@ -1180,26 +1199,47 @@ void Engine::LoadScene(const std::string &path) if (root["Assets"]) { + currentStep = "Loading Assets"; + currentDetail = "Parsing asset data..."; + loadingUI.Update(currentStep, currentDetail, 0.05f); + Logger::LogVerbose("[LoadScene] Loading Assets"); AssetManager::Load(root["Assets"]); } Logger::LogDebug("[LoadScene] Reseting Scene."); + currentStep = "Clearing Previous Scene"; + currentDetail = "Removing existing objects"; + loadingUI.Update(currentStep, currentDetail, 0.1f); + objects.clear(); Logger::LogDebug("[LoadScene] Recreating Objects"); + currentStep = "Creating Scene Objects"; const auto &objectArray = root["objects"]; + int objectIndex = 0; + int totalObjects = static_cast(objectArray.size()); + for (const auto &node : objectArray) { - auto obj = std::make_shared("[DefaultObject]"); + std::string objName = node["name"] ? node["name"].as() : ("Unnamed_" + std::to_string(objectIndex)); + currentDetail = "Loading: " + objName; + float progress = 0.1f + (0.75f * (objectIndex / static_cast(std::max(1, totalObjects)))); + loadingUI.Update(currentStep, currentDetail, progress); + + auto obj = std::make_shared(objName); obj->Load(node); objects.push_back(obj); + objectIndex++; } if (root["color_correction"]) { Logger::LogVerbose("[LoadScene] Loading Color Correction Attributes"); + currentStep = "Color Correction"; + currentDetail = "Applying settings..."; + loadingUI.Update(currentStep, currentDetail, 0.9f); ColorCorrection *cc = Renderer::GetColorCorrection(); const auto &data = root["color_correction"]; @@ -1220,7 +1260,13 @@ void Engine::LoadScene(const std::string &path) } } + currentStep = "Done"; + currentDetail = "Finalizing load..."; + loadingUI.Update(currentStep, currentDetail, 1.0f); + Logger::LogInfo("[LoadScene] Loaded scene: %s", root["scene_name"].as().c_str()); + + loadingUI.Destroy(); } void Engine::SaveState() diff --git a/src/src/Engine.h b/src/src/Engine.h index 2996911..0535f45 100644 --- a/src/src/Engine.h +++ b/src/src/Engine.h @@ -11,6 +11,7 @@ class ScriptComponent; class PhysicsComponent; class ParticleComponent; class AnimationComponent; +class TilemapComponent; class Engine { @@ -38,6 +39,7 @@ private: std::vector m_toDraw; std::vector m_scriptUpdates; std::vector m_animationsUpdates; + int m_OnUpdateCalls; std::vector> m_collectStack; diff --git a/src/src/Entitys/Object.cpp b/src/src/Entitys/Object.cpp index c8ceec7..c7a0bf8 100644 --- a/src/src/Entitys/Object.cpp +++ b/src/src/Entitys/Object.cpp @@ -161,6 +161,7 @@ void Object::Load(const YAML::Node &node) else if (type == "AnimationComponent") AddComponent()->Load(compNode); + } } diff --git a/src/src/Renderer.cpp b/src/src/Renderer.cpp index fe3e0a7..954fd1f 100644 --- a/src/src/Renderer.cpp +++ b/src/src/Renderer.cpp @@ -2,6 +2,8 @@ #include "Renderer.h" #include "Components/SpriteComponent.h" +#include "Components/TilemapComponent.h" + #include "utils/Shader.h" #include "core/utils/Logging.h" #include "core/utils/EngineConfig.h" @@ -444,6 +446,10 @@ void Renderer::End() glBindFramebuffer(GL_FRAMEBUFFER, 0); } + + + + void Renderer::ClearLights() { PROFILE_ENGINE_SCOPE("Renderer::ClearLights"); @@ -462,60 +468,24 @@ void Renderer::AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, floa s_LightsCount++; } -void Renderer::DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &worldPos, float zoom, const glm::vec2 &cameraPos) +void Renderer::DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &pos, float zoom, const glm::vec2 &cameraPos) { - PROFILE_ENGINE_SCOPE("Renderer::DrawTilemap"); - - if (!tilemap || tilemap->GetAtlasPath().empty()) + if (!tilemap || !tilemap->GetTexture()) return; - glm::ivec2 grid = tilemap->GetGridSize(); - glm::ivec2 tileSize = tilemap->GetTileSize(); - int cols = tilemap->GetAtlasCols(); - int rows = tilemap->GetAtlasRows(); + const TextureAtlas &atlas = tilemap->GetAtlas(); + const core::types::Vec2 &tileSize = tilemap->GetTileSize(); - const std::string &atlasPath = tilemap->GetAtlasPath(); - GLuint atlasTex = LoadTextureIfNeeded(atlasPath); - if (atlasTex == 0) - return; - - tilemapShader.Use(); - tilemapShader.SetVec2("uScreen", glm::vec2(width, height)); - tilemapShader.SetInt("uTex", 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, atlasTex); - - glBindVertexArray(quadVAO); - - for (int y = 0; y < grid.y; ++y) + for (const auto &[key, tileIndex] : tilemap->GetTileData()) { - for (int x = 0; x < grid.x; ++x) - { - int index = tilemap->GetTile(x, y); - if (index < 0 || index >= cols * rows) - continue; + int x, y; + TilemapComponent::UnpackCoord(key, x, y); - int atlasX = index % cols; - int atlasY = index / cols; + glm::vec2 tileWorldPos = glm::vec2(x * tileSize.x, y * tileSize.y); + glm::vec2 screenPos = (tileWorldPos - cameraPos) * zoom + glm::vec2(width, height) * 0.5f; - glm::vec2 uvMin = glm::vec2(atlasX, atlasY) / glm::vec2(cols, rows); - glm::vec2 uvMax = (glm::vec2(atlasX + 1, atlasY + 1)) / glm::vec2(cols, rows); - - tilemapShader.SetVec2("uUVMin", uvMin); - tilemapShader.SetVec2("uUVMax", uvMax); - - glm::vec2 tileWorld = worldPos + glm::vec2(x * tileSize.x, y * tileSize.y); - glm::vec2 screenPos = (tileWorld - cameraPos) * zoom + glm::vec2(width, height) * 0.5f; - - tilemapShader.SetVec2("uPos", screenPos); - tilemapShader.SetVec2("uSize", glm::vec2(tileSize) * zoom); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - s_DrawCalls++; - } + DrawTextureAtlas(&atlas, tileIndex, screenPos, 0.0f, zoom); } - - glBindVertexArray(0); } void Renderer::DrawQuad(const core::types::Vec2 &pos, const core::types::Vec2 &size, float rotation, const core::types::Color &color) @@ -615,7 +585,7 @@ void Renderer::DrawTextureAtlas(const TextureAtlas *atlas, int index, const core drawEntry.sprite = entry; drawEntry.shader = &unlitShader; drawEntry.useLighting = false; - drawEntry.usesUV = true; // <- Important! + drawEntry.usesUV = true; sortedDrawList.push_back(drawEntry); } diff --git a/src/src/Renderer.h b/src/src/Renderer.h index 40c9c09..022afba 100644 --- a/src/src/Renderer.h +++ b/src/src/Renderer.h @@ -60,7 +60,8 @@ public: static void End(); static void DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float zoom, glm::vec2 &CameraPos); - static void DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &worldPos, float zoom, const glm::vec2 &cameraPos); + static void DrawTilemap(TilemapComponent* tilemap, const glm::vec2& pos, float zoom, const glm::vec2& cameraPos); + static void AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, float intensity, float radius); static void ClearLights(); diff --git a/src/src/core/utils/LoadingWindow.cpp b/src/src/core/utils/LoadingWindow.cpp new file mode 100644 index 0000000..b4b193a --- /dev/null +++ b/src/src/core/utils/LoadingWindow.cpp @@ -0,0 +1,101 @@ +#define UNICODE +#define _UNICODE +#include "LoadingWindow.h" +#include + +static const wchar_t* kWindowClass = L"LoadingWndClass"; +static LoadingWindow* g_instance = nullptr; + +void LoadingWindow::Create(const std::string& title) +{ + g_instance = this; + windowTitle = std::wstring(title.begin(), title.end()); + + WNDCLASS wc = {}; + wc.lpfnWndProc = WndProc; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpszClassName = kWindowClass; + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + RegisterClass(&wc); + + hwnd = CreateWindowEx( + 0, kWindowClass, windowTitle.c_str(), + WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME, + CW_USEDEFAULT, CW_USEDEFAULT, 500, 200, + nullptr, nullptr, GetModuleHandle(nullptr), nullptr + ); + + ShowWindow(hwnd, SW_SHOWNORMAL); + UpdateWindow(hwnd); +} + +void LoadingWindow::Update(const std::string& step, const std::string& detail, float progress) +{ + currentStep = step; + currentDetail = detail; + currentProgress = std::clamp(progress, 0.0f, 1.0f); + + InvalidateRect(hwnd, nullptr, TRUE); + UpdateWindow(hwnd); + + MSG msg; + while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +void LoadingWindow::Destroy() +{ + if (hwnd) + { + DestroyWindow(hwnd); + hwnd = nullptr; + } + UnregisterClass(kWindowClass, GetModuleHandle(nullptr)); +} + +LRESULT CALLBACK LoadingWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_PAINT && g_instance) + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + RECT r; + GetClientRect(hWnd, &r); + + std::wstring step = std::wstring(g_instance->currentStep.begin(), g_instance->currentStep.end()); + std::wstring detail = std::wstring(g_instance->currentDetail.begin(), g_instance->currentDetail.end()); + + SetBkMode(hdc, TRANSPARENT); + SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); + + RECT textRect = r; + textRect.top += 10; + textRect.left += 20; + + DrawTextW(hdc, step.c_str(), -1, &textRect, DT_LEFT | DT_TOP | DT_SINGLELINE); + textRect.top += 30; + DrawTextW(hdc, detail.c_str(), -1, &textRect, DT_LEFT | DT_TOP | DT_SINGLELINE); + + // Progress bar background + RECT progressRect = { r.left + 20, r.bottom - 50, r.right - 20, r.bottom - 30 }; + HBRUSH bg = CreateSolidBrush(RGB(200, 200, 200)); + FillRect(hdc, &progressRect, bg); + DeleteObject(bg); + + // Progress bar fill + int barWidth = static_cast((progressRect.right - progressRect.left) * g_instance->currentProgress); + RECT fillRect = { progressRect.left, progressRect.top, progressRect.left + barWidth, progressRect.bottom }; + HBRUSH fill = CreateSolidBrush(RGB(70, 140, 255)); + FillRect(hdc, &fillRect, fill); + DeleteObject(fill); + + EndPaint(hWnd, &ps); + return 0; + } + + return DefWindowProc(hWnd, msg, wParam, lParam); +} diff --git a/src/src/core/utils/LoadingWindow.h b/src/src/core/utils/LoadingWindow.h new file mode 100644 index 0000000..74fae70 --- /dev/null +++ b/src/src/core/utils/LoadingWindow.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include + +class LoadingWindow { +public: + void Create(const std::string& title = "Loading..."); + void Update(const std::string& step, const std::string& detail, float progress); // NEW + void Destroy(); + +private: + HWND hwnd = nullptr; + std::wstring windowTitle; + std::string currentStep; + std::string currentDetail; + float currentProgress = 0.0f; + + static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +}; diff --git a/src/src/core/utils/Texture.h b/src/src/core/utils/Texture.h index d4af952..e3dc41e 100644 --- a/src/src/core/utils/Texture.h +++ b/src/src/core/utils/Texture.h @@ -11,7 +11,7 @@ enum class RenderType { class Texture { public: Texture(const std::string& path); - Texture(uint64_t uaid); // New constructor using UAID + Texture(uint64_t uaid); ~Texture(); unsigned int GetID() const; diff --git a/src/src/core/utils/TextureAtlas.h b/src/src/core/utils/TextureAtlas.h index 147c2a9..02536a6 100644 --- a/src/src/core/utils/TextureAtlas.h +++ b/src/src/core/utils/TextureAtlas.h @@ -6,8 +6,8 @@ struct TextureAtlas { std::shared_ptr texture; - int frameWidth = 1; - int frameHeight = 1; + int frameWidth = 32; + int frameHeight = 32; TextureAtlas() = default; @@ -23,6 +23,11 @@ struct TextureAtlas frameHeight = texelHeight; } + core::types::Vec2 GetTexelSize() const + { + return core::types::Vec2(frameWidth, frameHeight); + } + core::types::Vec2 GetFrameUV(int index) const { if (!texture || frameWidth <= 0 || frameHeight <= 0) diff --git a/src/src/editor/windows/Inspector.cpp b/src/src/editor/windows/Inspector.cpp index 49f6a84..4626e02 100644 --- a/src/src/editor/windows/Inspector.cpp +++ b/src/src/editor/windows/Inspector.cpp @@ -10,6 +10,9 @@ #include "../../components/ParticleComponent.h" #include "../../components/AnimationComponent.h" +#include "imgui.h" +#include "imgui_internal.h" + void DrawInspectorUI(std::shared_ptr selected) { PROFILE_ENGINE_SCOPE("Engine::DrawInspectorUI"); @@ -276,30 +279,28 @@ void DrawInspectorUI(std::shared_ptr selected) { std::string texFile = "(None)"; - if (const auto* asset = AssetManager::GetAssetByID(anim->GetTextureUAID())) + if (const auto *asset = AssetManager::GetAssetByID(anim->GetTextureUAID())) texFile = GetFilenameFromPath(asset->path); - + ImGui::Text("%s", texFile.c_str()); - + if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ASSET_TEXTURE")) + if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("ASSET_TEXTURE")) { - uint64_t droppedUAID = *(const uint64_t*)payload->Data; + uint64_t droppedUAID = *(const uint64_t *)payload->Data; anim->SetTextureAtlas(droppedUAID, frameWidth, frameHeight, frameDuration, startFrame, endFrame); } ImGui::EndDragDropTarget(); } - + if (ImGui::SmallButton("Load##AnimTex")) { std::string path = OpenFileDialog(FileDialogType::Images); if (const auto *asset = AssetManager::GetAssetByPath(path)) anim->SetTextureAtlas(asset->uaid, frameWidth, frameHeight, frameDuration, startFrame, endFrame); - } } - ImGui::InputInt("Texel Width", &frameWidth); ImGui::InputInt("Texel Height", &frameHeight); @@ -312,7 +313,6 @@ void DrawInspectorUI(std::shared_ptr selected) { if (const auto *asset = AssetManager::GetAssetByID(anim->GetTextureUAID())) anim->SetTextureAtlas(asset->uaid, frameWidth, frameHeight, frameDuration, startFrame, endFrame); - } ImGui::SeparatorText("Playback"); @@ -424,16 +424,65 @@ void DrawInspectorUI(std::shared_ptr selected) { ImGui::SeparatorText("Tilemap Component"); - ImGui::Text("Refer to Tilemap Editor Window"); + static int texelWidth = (int)tilemap->GetAtlas().GetTexelSize().x; + static int texelHeight = (int)tilemap->GetAtlas().GetTexelSize().y; + static core::types::Vec2 tileSize = tilemap->GetTileSize(); + uint64_t uaid = tilemap->GetTextureUAID(); + const auto *asset = AssetManager::GetAssetByID(uaid); + + ImGui::SeparatorText("Texture Atlas"); + + // --- Atlas file name or (None) --- + std::string texFile = asset ? GetFilenameFromPath(asset->path) : "(None)"; + ImGui::Text("%s", texFile.c_str()); + + // --- Drag and Drop Target --- + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("ASSET_TEXTURE")) + { + if (payload->DataSize == sizeof(uint64_t)) + { + uint64_t newUaid = *(const uint64_t *)payload->Data; + tilemap->SetTextureAtlas(newUaid, texelWidth, texelHeight); + } + } + ImGui::EndDragDropTarget(); + } + + // --- Load via file dialog --- + if (ImGui::SmallButton("Load##TilemapTex")) + { + std::string path = OpenFileDialog(FileDialogType::Images); + if (const auto *loaded = AssetManager::GetAssetByPath(path)) + tilemap->SetTextureAtlas(loaded->uaid, texelWidth, texelHeight); + } + + + // --- Tile Size --- + if (ImGui::DragFloat2("Tile Size", &tileSize.x, 1.0f, 1.0f, 512.0f)) + tilemap->SetTileSize(tileSize); + + // --- Apply changes to atlas --- + if (ImGui::Button("Apply")) + { + if (const auto *texAsset = AssetManager::GetAssetByID(tilemap->GetTextureUAID())) + tilemap->SetTextureAtlas(texAsset->uaid, texelWidth, texelHeight); + } + + // --- Atlas Preview --- + if (tilemap->GetTexture() && tilemap->GetTexture()) + { + float previewSize = ImGui::GetContentRegionAvail().x; + ImGui::SeparatorText("Atlas Preview"); + ImGui::Image((ImTextureID)(uintptr_t)tilemap->GetTexture()->GetID(), ImVec2(previewSize, previewSize)); + } + + ImGui::Separator(); if (ImGui::Button("Remove TilemapComponent")) selected->RemoveComponent(); } ImGui::End(); - - if (auto tilemap = selected->GetComponent()) - { - tilemap->DrawEditorUI(); - } } \ No newline at end of file