diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index e70b6e00..ec1221df 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -26,7 +26,8 @@ jobs: sudo apt-get install -y xorg-dev llvm-dev iwyu clang++-15 libx11-xcb-dev libxcb-render-util0-dev \ libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev \ libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev libxcb-dri3-dev \ - libxcb-util-dev libxcb-cursor-dev + libxcb-util-dev libxcb-cursor-dev libx11-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev \ + libxcb-glx0-dev libxcb-dri2-0-dev libxcb-present-dev libxcb-composite0-dev libxcb-ewmh-dev libxcb-res0-dev pip install conan diff --git a/editor/editor.cpp b/editor/editor.cpp index 8d20910b..757c3672 100644 --- a/editor/editor.cpp +++ b/editor/editor.cpp @@ -173,15 +173,16 @@ Editor::MouseButtonCallback(MouseButtonEvent& event) gui_.ObjectUnselected(object); } - - auto& firstObject = currentLevel_->GetGameObjectRef(selectedObjects.front()); + + selectedObjects_ = selectedObjects; + auto& firstObject = currentLevel_->GetGameObjectRef(selectedObjects_.front()); auto gizmoPos = firstObject.GetCenteredPosition(); glm::vec2 min = gizmoPos; glm::vec2 max = gizmoPos; - for (const auto object : selectedObjects) + for (const auto object : selectedObjects_) { - gui_.ObjectSelected(object); + gui_.ObjectSelected(object, true); const auto& objectPos = currentLevel_->GetGameObjectRef(object).GetCenteredPosition(); @@ -196,7 +197,7 @@ Editor::MouseButtonCallback(MouseButtonEvent& event) }; } - selectedObjects_ = selectedObjects; + gizmoActive_ = true; gizmo_.Show(); @@ -495,6 +496,10 @@ Editor::HandleGameObjectClicked(Object::ID newSelectedGameObject, bool groupSele } selectedObjects_.push_back(newSelectedGameObject); + if (groupSelect) + { + gui_.ObjectSelected(newSelectedGameObject, groupSelect); + } } movementOnGameObject_ = !fromGUI; @@ -570,7 +575,7 @@ Editor::SelectGameObject(Object::ID newSelectedGameObject) } currentSelectedGameObject_ = newSelectedGameObject; - gui_.ObjectSelected(currentSelectedGameObject_); + gui_.ObjectSelected(currentSelectedGameObject_, false); // Make sure to render animation points if needed auto& gameObject = currentLevel_->GetGameObjectRef(newSelectedGameObject); diff --git a/editor/gui/editor_gui.cpp b/editor/gui/editor_gui.cpp index 46320236..94e1212c 100644 --- a/editor/gui/editor_gui.cpp +++ b/editor/gui/editor_gui.cpp @@ -4,6 +4,7 @@ #include "game_object.hpp" #include "helpers.hpp" #include "icons.hpp" +#include "input/input_manager.hpp" #include "renderer/renderer.hpp" #include "renderer/shader.hpp" #include "renderer/texture.hpp" @@ -11,7 +12,6 @@ #include "renderer/vulkan_common.hpp" #include "types.hpp" #include "utils/file_manager.hpp" -#include "input/input_manager.hpp" #include #include @@ -27,6 +27,32 @@ EditorGUI::EditorGUI(Editor& parent) : parent_(parent) { } +void +EditorGUI::RecalculateCommonRenderLayerAndColision() +{ + if (selectedObjects_.empty()) + { + return; + } + + const auto& [idFirst, collisionFirst, layerFirst] = selectedObjects_.front(); + commonRenderLayer_ = {true, layerFirst}; + commonCollision_ = {true, collisionFirst}; + + for (uint32_t idx = 1; idx < selectedObjects_.size(); idx++) + { + const auto& [id, collision, layer] = selectedObjects_.at(idx); + if (commonRenderLayer_.first and (layer != commonRenderLayer_.second)) + { + commonRenderLayer_.first = false; + } + if (commonCollision_.first and (collision != commonCollision_.second)) + { + commonCollision_.first = false; + } + } +} + void EditorGUI::KeyCallback(KeyEvent& event) { @@ -146,7 +172,7 @@ EditorGUI::UpdateUI() } ImGui::Render(); - + setScrollTo_ = {}; } @@ -183,25 +209,39 @@ EditorGUI::LevelLoaded(const std::shared_ptr< Level >& loadedLevel) } void -EditorGUI::ObjectSelected(Object::ID ID) +EditorGUI::ObjectSelected(Object::ID ID, bool groupSelect) { objectsInfo_[ID].second = true; setScrollTo_ = ID; - currentlySelectedGameObject_ = ID; + const auto& gameObject = parent_.GetLevel().GetGameObjectRef(ID); + selectedObjects_.emplace_back( + ID, gameObject.GetHasCollision(), gameObject.GetSprite().GetRenderInfo().layer); + RecalculateCommonRenderLayerAndColision(); + + if (not groupSelect) + { + currentlySelectedGameObject_ = ID; + } } void EditorGUI::ObjectUnselected(Object::ID ID) { - objectsInfo_[currentlySelectedGameObject_].second = false; - currentlySelectedGameObject_ = Object::INVALID_ID; - - objectsInfo_[ID].second = false; if (currentlySelectedGameObject_ == ID) { currentlySelectedGameObject_ = Object::INVALID_ID; } + else + { + objectsInfo_[ID].second = false; + + selectedObjects_.erase(stl::find_if(selectedObjects_, [ID](const auto& obj) { + const auto [id, collision, layer] = obj; + return id == ID; + })); + RecalculateCommonRenderLayerAndColision(); + } } void diff --git a/editor/gui/editor_gui.hpp b/editor/gui/editor_gui.hpp index 09dd830c..672ff312 100644 --- a/editor/gui/editor_gui.hpp +++ b/editor/gui/editor_gui.hpp @@ -54,7 +54,7 @@ class EditorGUI : public InputListener LevelLoaded(const std::shared_ptr< Level >& loadedLevel); void - ObjectSelected(Object::ID ID); + ObjectSelected(Object::ID ID, bool groupSelect); void ObjectUnselected(Object::ID ID); @@ -90,12 +90,17 @@ class EditorGUI : public InputListener void RenderGameObjectContent(); + void + RenderGroupSelectModifications(); + void RenderCreateNewLevelWindow(); void RenderExitWindow(); + void RecalculateCommonRenderLayerAndColision(); + private: static void PrepareResources(); @@ -122,6 +127,9 @@ class EditorGUI : public InputListener // Data needed for loaded objects menu std::unordered_map< Object::ID, std::pair< std::string, bool > > objectsInfo_ = {}; Object::ID setScrollTo_ = Object::INVALID_ID; + std::pair< bool, int32_t > commonRenderLayer_ = {false, 0}; + std::pair< bool, bool > commonCollision_ = {false, false}; + std::vector > selectedObjects_ = {}; }; } // namespace looper diff --git a/editor/gui/editor_gui_object.cpp b/editor/gui/editor_gui_object.cpp index 00c16ab6..2871877e 100644 --- a/editor/gui/editor_gui_object.cpp +++ b/editor/gui/editor_gui_object.cpp @@ -80,20 +80,127 @@ EditorGUI::RenderSelectedObjectsMenu() ImGui::EndChild(); - // If the user selects Objects from the List if (currentlySelectedGameObject_ != Object::INVALID_ID) { RenderGameObjectContent(); } + else + { + RenderGroupSelectModifications(); + } ImGui::End(); } +void +EditorGUI::RenderGroupSelectModifications() +{ + ImGui::SetNextItemOpen(true); + if (ImGui::CollapsingHeader("Group Action")) + { + if (ImGui::BeginTable("ObjectsTable", 3)) + { + const auto totalWidth = ImGui::GetContentRegionAvail().x; + + ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthStretch, 0.55f * totalWidth); + ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch, 0.35f * totalWidth); + ImGui::TableSetupColumn("Button", ImGuiTableColumnFlags_WidthStretch, 0.10f * totalWidth); + + CreateActionRowLabel( + "RenderLayer", + [this] { + const auto items = + std::to_array< std::string >({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##GroupSetLayer", + fmt::format("{}", commonRenderLayer_.second).c_str())) + { + for (const auto& item : items) + { + if (ImGui::Selectable(item.c_str())) + { + parent_.AddToWorkQueue([item, this] { + const auto newLayer = std::stoi(item); + const auto& gameObjects = parent_.GetSelectedObjects(); + for (auto object : gameObjects) + { + parent_.GetLevel() + .GetGameObjectRef(object) + .GetSprite() + .ChangeRenderLayer(newLayer); + } + + for (auto& [id, collision, layer] : selectedObjects_) + { + layer = newLayer; + } + + commonRenderLayer_ = {true, newLayer}; + }); + } + } + ImGui::EndCombo(); + } + }, + [this] { + if (not commonRenderLayer_.first) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.5f, 0.0f, 1.0f}); + ImGui::Button(ICON_FA_CIRCLE_EXCLAMATION "##LayerNotCommon"); + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Not all selcted Objects have the same layer set!"); + } + ImGui::PopStyleColor(1); + } + }); + CreateActionRowLabel( + "Has Collision", + [this] { + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::Checkbox("##GroupHasCollision", &commonCollision_.second)) + { + parent_.AddToWorkQueue([this] { + const auto& gameObjects = parent_.GetSelectedObjects(); + for (auto object : gameObjects) + { + parent_.GetLevel().GetGameObjectRef(object).SetHasCollision( + commonCollision_.second); + } + + for (auto& [id, collision, layer] : selectedObjects_) + { + collision = commonCollision_.second; + } + commonCollision_.first = true; + + parent_.GetLevel().UpdateCollisionTexture(); + }); + } + }, + [this] { + if (not commonCollision_.first) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.5f, 0.0f, 1.0f}); + ImGui::Button(ICON_FA_CIRCLE_EXCLAMATION "##CollisionNotCommon"); + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Not all selected Objects have the same collision set!"); + } + ImGui::PopStyleColor(1); + } + }); + + ImGui::EndTable(); + } + } +} + void EditorGUI::RenderGameObjectContent() { ImGui::SetNextItemOpen(true); - if (ImGui::CollapsingHeader("General")) + if (ImGui::CollapsingHeader("Selected")) { auto& gameObject = currentLevel_->GetGameObjectRef(currentlySelectedGameObject_); @@ -114,23 +221,29 @@ EditorGUI::RenderGameObjectContent() if (ImGui::BeginTable("ObjectTable", 2)) { CreateActionRowLabel("RenderLayer", [this, &gameObject] { - const auto items = std::to_array< std::string >( - {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}); + const auto items = + std::to_array< std::string >({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}); if (ImGui::BeginCombo( - "##combo", + "##ObjectSetLayer", fmt::format("{}", gameObject.GetSprite().GetRenderInfo().layer).c_str())) { for (const auto& item : items) { if (ImGui::Selectable(item.c_str())) { - parent_.AddToWorkQueue([&gameObject, item] { - const auto layer = std::stoi(item); - const auto oldLayer = gameObject.GetSprite().GetRenderInfo().layer; - gameObject.GetSprite().ChangeRenderLayer(layer); - - renderer::SetupVertexBuffer(oldLayer); - renderer::SetupVertexBuffer(layer); + parent_.AddToWorkQueue([&gameObject, item, this] { + const auto newLayer = std::stoi(item); + gameObject.GetSprite().ChangeRenderLayer(newLayer); + + auto obj = + stl::find_if(selectedObjects_, + [curID = currentlySelectedGameObject_](const auto& obj) { + auto [id, collision, layer] = obj; + return id == curID; + }); + auto& [objID, objCollision, objLayer] = *obj; + objLayer = newLayer; + RecalculateCommonRenderLayerAndColision(); }); } } @@ -144,6 +257,14 @@ EditorGUI::RenderGameObjectContent() if (ImGui::Checkbox("##Has Collision", &collision)) { gameObject.SetHasCollision(collision); + auto obj = stl::find_if(selectedObjects_, + [curID = currentlySelectedGameObject_](const auto& obj) { + auto [id, hasCollision, layer] = obj; + return id == curID; + }); + auto& [objID, objCollision, objLayer] = *obj; + objCollision = collision; + RecalculateCommonRenderLayerAndColision(); parent_.GetLevel().UpdateCollisionTexture(); } }); @@ -174,11 +295,10 @@ EditorGUI::RenderGameObjectContent() }); DrawWidget("Rotate", [&gameObject]() { - auto rotation = - gameObject.GetSprite().GetRotation(renderer::RotationType::degrees); + auto rotation = gameObject.GetSprite().GetRotation(renderer::RotationType::degrees); if (ImGui::InputFloat("##Rotate", &rotation, - glm::degrees(renderer::Sprite::ROTATION_RANGE.first), - glm::degrees(renderer::Sprite::ROTATION_RANGE.second))) + glm::degrees(renderer::Sprite::ROTATION_RANGE.first), + glm::degrees(renderer::Sprite::ROTATION_RANGE.second))) { gameObject.Rotate(glm::radians(rotation)); } diff --git a/engine/game/level.cpp b/engine/game/level.cpp index deddc5fa..5c79c990 100644 --- a/engine/game/level.cpp +++ b/engine/game/level.cpp @@ -562,10 +562,9 @@ Level::LoadPremade(const std::string& fileName, const glm::ivec2& size) locked_ = false; levelSize_ = size; - background_.SetSpriteTextured(glm::vec3(static_cast< float >(levelSize_.x) / 2.0f, - static_cast< float >(levelSize_.y) / 2.0f, - renderer::LAYER_10), - size, fileName); + background_.SetSpriteTextured(glm::vec2(static_cast< float >(levelSize_.x) / 2.0f, + static_cast< float >(levelSize_.y) / 2.0f), + size, fileName, 10); baseTexture_ = background_.GetTexture()->GetID(); } diff --git a/engine/renderer/sprite.cpp b/engine/renderer/sprite.cpp index a339b3df..98367742 100644 --- a/engine/renderer/sprite.cpp +++ b/engine/renderer/sprite.cpp @@ -39,9 +39,13 @@ Sprite::ChangeRenderLayer(int32_t newLayer) } const auto transformMat = ComputeModelMat(); + const auto oldLayer = renderInfo_.layer; renderInfo_ = MeshLoaded(vertices_, textures_, transformMat, currentState_.color_); changed_ = true; + + SetupVertexBuffer(oldLayer); + SetupVertexBuffer(newLayer); } void