From d893849dc3a6aa0a947fcc95c7476c7b33415eb8 Mon Sep 17 00:00:00 2001 From: Joey Pollack Date: Tue, 13 Sep 2022 15:57:18 -0400 Subject: [PATCH] Basic parent/child relation working with entities --- docs/tasks/core.todo | 4 + src/renderer/renderer2D.cpp | 2 +- src/renderer/renderer2D.h | 1 + src/renderer/shaders/quad.vert | 2 +- .../editor/panels/properties_view.cpp | 12 + src/run_modes/editor/panels/world_tree.cpp | 229 +++++++++--------- src/run_modes/editor/panels/world_tree.h | 6 + src/world/components.h | 12 + src/world/entity.cpp | 81 ++++++- src/world/entity.h | 16 +- src/world/world.cpp | 3 + test_data/engine_state.json | 2 +- 12 files changed, 249 insertions(+), 121 deletions(-) diff --git a/docs/tasks/core.todo b/docs/tasks/core.todo index 237bb41..c2d4d98 100644 --- a/docs/tasks/core.todo +++ b/docs/tasks/core.todo @@ -34,6 +34,10 @@ Core: ✔ Read the window size and position on shutdown and write these to the state file @done (2/8/2022, 4:39:37 PM) Graphics: + ☐ Implement parent transform for all draw methods + ☐ Ellipse + ☐ Lines + ☐ Text Re-write the renderer: ✔ Implement BeginScene and EndScene - these are called from the World OnRender @done(22-09-07 14:47) diff --git a/src/renderer/renderer2D.cpp b/src/renderer/renderer2D.cpp index ceeb0da..e76a03d 100644 --- a/src/renderer/renderer2D.cpp +++ b/src/renderer/renderer2D.cpp @@ -131,7 +131,7 @@ namespace lunarium float parent_idx = -1.0f; if (parent_transform != glm::mat4(1.0f)) { - if (mQuadData.Parents.size() > 15) + if (mQuadData.Parents.size() >= mQuadData.ParentSlots) Flush(FlushMode::Quads); mQuadData.Parents.push_back(parent_transform); diff --git a/src/renderer/renderer2D.h b/src/renderer/renderer2D.h index a606d55..508e048 100644 --- a/src/renderer/renderer2D.h +++ b/src/renderer/renderer2D.h @@ -115,6 +115,7 @@ namespace lunarium const int MaxVertices = MaxQuads * 4; const int MaxIndices = MaxQuads * 6; const int TextureSlots = 32; + const int ParentSlots = 32; std::vector LoadedTextures; Texture* WhiteTexture; diff --git a/src/renderer/shaders/quad.vert b/src/renderer/shaders/quad.vert index e7cd7fd..7b4cfaa 100644 --- a/src/renderer/shaders/quad.vert +++ b/src/renderer/shaders/quad.vert @@ -16,7 +16,7 @@ layout (location = 2) flat out int f_tex_index; layout (location = 3) flat out int f_tex_is_grey; uniform mat4 projview; -uniform mat4 parents[16]; +uniform mat4 parents[32]; void main() { diff --git a/src/run_modes/editor/panels/properties_view.cpp b/src/run_modes/editor/panels/properties_view.cpp index a47c78c..362918f 100644 --- a/src/run_modes/editor/panels/properties_view.cpp +++ b/src/run_modes/editor/panels/properties_view.cpp @@ -151,6 +151,18 @@ namespace lunarium { namespace editor void PropertiesView::ShowEntity() { + ImGui::Text("UUID: %ull", (u64)mpSelectedEntity->GetUUID()); + char buffer[256] = { 0 }; + strcpy(buffer, mpSelectedEntity->GetName().c_str()); + ImGui::Text("Name:"); + ImGui::SameLine(); + if (ImGui::InputText("##Entity Name", buffer, 256)) + { + mpSelectedEntity->SetName(buffer); + } + + ImGui::Separator(); + ImGuiExt::TextCentered("Components"); float spacing_y = 12.0f; ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, spacing_y)); diff --git a/src/run_modes/editor/panels/world_tree.cpp b/src/run_modes/editor/panels/world_tree.cpp index 47db2d2..b79fe36 100644 --- a/src/run_modes/editor/panels/world_tree.cpp +++ b/src/run_modes/editor/panels/world_tree.cpp @@ -16,145 +16,154 @@ #include #include -namespace lunarium +namespace lunarium { namespace editor { - namespace editor + WorldTree::WorldTree(Editor *editor) + : Panel("World Tree", PanelDockZone::DDZ_LEFT, true, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar), + mpEditor(editor), + // TODO: Temp world created here + mpWorld(nullptr), + mpSelectedEntity(nullptr), mNewChild(false), mParentEnt(nullptr) { - WorldTree::WorldTree(Editor *editor) - : Panel("World Tree", PanelDockZone::DDZ_LEFT, true, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar), - mpEditor(editor), - // TODO: Temp world created here - mpWorld(nullptr), - mpSelectedEntity(nullptr) - { - AddPopup(PopUp::NEW_ENTITY, "New Entity", [](Panel *p) + AddPopup(PopUp::NEW_ENTITY, "New Entity", [](Panel *p) + { + WorldTree* pWT = (WorldTree*)p; + if (!pWT->mpWorld) { - WorldTree* pWT = (WorldTree*)p; - if (!pWT->mpWorld) - { - return false; - } + return false; + } + + char name_buf[64] = "NewEntity"; + ImGui::TextUnformatted("Entity Name: "); + ImGui::SameLine(); + if (ImGui::InputText("##Entity Name", name_buf, 64, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) + { + LUUID id = pWT->mpWorld->CreateEntity(); + pWT->mpWorld->GetEntity(id)->SetName(name_buf); - char name_buf[64] = "NewEntity"; - ImGui::TextUnformatted("Entity Name: "); - ImGui::SameLine(); - if (ImGui::InputText("##Entity Name", name_buf, 64, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) + if (pWT->mNewChild) { - LUUID id = pWT->mpWorld->CreateEntity(); - pWT->mpWorld->GetEntity(id)->AddComponent(); - TagComponent& tc = pWT->mpWorld->GetEntity(id)->GetComponent(); - tc.Info = name_buf; - return false; + pWT->mParentEnt->AddChild(id); } - if (Core::Input().IsKeyDown(KeyCode::ESCAPE, true)) - { - return false; - } + pWT->mNewChild = false; + pWT->mParentEnt = nullptr; + return false; + } - return true; - }).LogIfFailed(Editor::LogCat); - } + if (Core::Input().IsKeyDown(KeyCode::ESCAPE, true)) + { + pWT->mNewChild = false; + return false; + } - void WorldTree::SetWorld(lunarium::World *pWorld) - { - mpWorld = pWorld; - mpSelectedEntity = nullptr; - } + return true; + }).LogIfFailed(Editor::LogCat); + } - lunarium::World *WorldTree::GetWorld() + void WorldTree::SetWorld(lunarium::World *pWorld) + { + mpWorld = pWorld; + mpSelectedEntity = nullptr; + } + + lunarium::World *WorldTree::GetWorld() + { + return mpWorld; + } + + void WorldTree::DoFrame() + { + Entity *pSelection = nullptr; + if (!mpWorld) { - return mpWorld; + + ImGui::Text("No World Loaded"); + return; } - void WorldTree::DoFrame() + ImGui::SetNextItemOpen(true); + + if (ImGui::TreeNode("World Root")) { - Entity *pSelection = nullptr; - if (!mpWorld) + // List all world entities + // This will fail if we end up implementing child entities + // In that case this needs to become recursive + int idx = 0; + for (auto iter = mpWorld->EntitiesBegin(); !mpWorld->EntitiesIsEnd(iter); iter++, idx++) { - - ImGui::Text("No World Loaded"); - return; + if (!(*iter)->HasComponent()) + { + ShowEntity((*iter)); + } } - ImGui::SetNextItemOpen(true); - - if (ImGui::TreeNode("World Root")) - { - // List all world entities - // This will fail if we end up implementing child entities - // In that case this needs to become recursive - int idx = 0; - for (auto iter = mpWorld->EntitiesBegin(); !mpWorld->EntitiesIsEnd(iter); iter++, idx++) - { - std::string name = "Entity"; - name += std::to_string(idx); - if ((*iter)->HasComponent()) - { - name = (*iter)->GetComponent().Info; - } + ImGui::TreePop(); + } - if ((*iter)->HasChildren()) - { - // TODO: Recurse through children - if (ImGui::TreeNode(name.c_str())) - { - ImGui::TreePop(); - } - } - else - { - bool is_selected = false; - if ((*iter) == mpSelectedEntity) - { - is_selected = true; - } - if (ImGui::Selectable(name.c_str(), &is_selected)) - { - mpSelectedEntity = (*iter); - if (is_selected) - { - mpEditor->OnEntitySelect(mpSelectedEntity); - } - else - { - mpEditor->OnEntitySelect(nullptr); - mpSelectedEntity = nullptr; - } - } - } - } + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && ImGui::IsWindowHovered()) + { + mpSelectedEntity = nullptr; + mpEditor->OnEntitySelect(nullptr); + } - ImGui::TreePop(); - } + DoContextMenu(); + } - if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && ImGui::IsWindowHovered()) + + void WorldTree::ShowEntity(Entity* pEnt) + { + bool was_clicked = false; + bool was_right_clicked = false; + if (ImGui::TreeNode(pEnt->GetName().c_str())) + { + was_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left); + was_right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right); + + const std::vector& children = pEnt->GetChildren(); + for (auto iter = children.begin(); iter != children.end(); iter++) { - mpSelectedEntity = nullptr; - mpEditor->OnEntitySelect(nullptr); + Entity* pe = mpWorld->GetEntity((*iter)); + ShowEntity(pe); } - DoContextMenu(); + ImGui::TreePop(); + } + else + { + was_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left); + was_right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right); } - void WorldTree::DoContextMenu() + if (was_clicked) + { + mpSelectedEntity = pEnt; + mpEditor->OnEntitySelect(pEnt); + } + if (was_right_clicked) { - if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight)) + mNewChild = true; + mParentEnt = pEnt; + } + } + + void WorldTree::DoContextMenu() + { + if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight)) + { + if (ImGui::Button("Create New Entity")) { - if (ImGui::Button("Create New Entity")) + if (!mpWorld) { - if (!mpWorld) - { - Logger::Warn(Editor::LogCat, "Can not create new entity - World pointer is null"); - } - else - { - OpenPopup(PopUp::NEW_ENTITY).LogIfFailed(Editor::LogCat); - } + Logger::Warn(Editor::LogCat, "Can not create new entity - World pointer is null"); + } + else + { + OpenPopup(PopUp::NEW_ENTITY).LogIfFailed(Editor::LogCat); } - ImGui::EndPopup(); } + ImGui::EndPopup(); } - } -} \ No newline at end of file + +}} \ No newline at end of file diff --git a/src/run_modes/editor/panels/world_tree.h b/src/run_modes/editor/panels/world_tree.h index 5ead871..0083b2d 100644 --- a/src/run_modes/editor/panels/world_tree.h +++ b/src/run_modes/editor/panels/world_tree.h @@ -35,6 +35,9 @@ namespace editor Editor* mpEditor; Entity* mpSelectedEntity; + // Helpers + void ShowEntity(Entity* pEnt); + // Context menus void DoContextMenu(); @@ -47,6 +50,9 @@ namespace editor { NEW_ENTITY, }; + + bool mNewChild; + Entity* mParentEnt; }; } } diff --git a/src/world/components.h b/src/world/components.h index 2069f3e..e05e17d 100644 --- a/src/world/components.h +++ b/src/world/components.h @@ -81,6 +81,18 @@ namespace lunarium BlockOutComponent() = default; BlockOutComponent(const BlockOutComponent&) = default; }; + + ///////////////////////////////////////////////////////////////////// + // UTILITY COMPONENTS + ///////////////////////////////////////////////////////////////////// + // These components are for internal use only. They do not show up in the editor. + struct ParentEntityComponent + { + LUUID parent; + + ParentEntityComponent() = default; + ParentEntityComponent(const ParentEntityComponent&) = default; + }; } #endif // LUNARIUM_COMPONENTS_H_ \ No newline at end of file diff --git a/src/world/entity.cpp b/src/world/entity.cpp index 33a3f9c..2a4fa5a 100644 --- a/src/world/entity.cpp +++ b/src/world/entity.cpp @@ -14,7 +14,7 @@ namespace lunarium { Entity::Entity(World& w) - : mHandle(entt::null), mWorld(w) + : mHandle(entt::null), mWorld(w), mParent(0), mParentSet(false) { mUUID = UUID::GetNewID(); Init(); @@ -42,6 +42,16 @@ namespace lunarium return mUUID; } + std::string Entity::GetName() const + { + return mName; + } + + void Entity::SetName(std::string name) + { + mName = name; + } + bool Entity::HasChildren() const { @@ -49,9 +59,52 @@ namespace lunarium } - void Entity::AddChild(Entity* pChild) + void Entity::AddChild(LUUID child) + { + Entity* e = mWorld.GetEntity(child); + e->AddComponent(mUUID); + mChildren.push_back(child); + } + + + void Entity::RemoveChild(LUUID child) + { + Entity* e = mWorld.GetEntity(child); + e->ClearParent(); + for (auto iter = mChildren.begin(); iter != mChildren.end(); iter++) + { + if ((*iter) == child) + { + mChildren.erase(iter); + break; + } + } + } + + + const std::vector& Entity::GetChildren() const { - mChildren.push_back(pChild); + return mChildren; + } + + bool Entity::HasParent() const + { + return mParentSet; + } + + LUUID Entity::GetParent() const + { + return mParent; + } + + void Entity::SetParent(LUUID parent) + { + mParent = parent; + } + + void Entity::ClearParent() + { + mParentSet = false; } @@ -63,6 +116,7 @@ namespace lunarium #if !BUILD_NO_EDITOR // Only does this when this is an editor build node["UUID"] = mUUID; + node["name"] = mName; auto& components = node["components"]; if (HasComponent()) @@ -151,6 +205,15 @@ namespace lunarium components.emplace_back(blockout); } + if (HasComponent()) + { + nlohmann::ordered_json parent; + ParentEntityComponent& comp = GetComponent(); + parent["type_name"] = "ParentEntityComponent"; + parent["UUID"] = (u64)comp.parent; + components.emplace_back(parent); + } + // TODO: ADD CODE TO SERIALIZE ANY NEW COMPONENTS @@ -160,7 +223,7 @@ namespace lunarium for (int i = 0; i < mChildren.size(); i++) { nlohmann::ordered_json child; - mChildren[i]->Serialize(child).LogIfFailed(LogCategory::GAME_SYSTEM); + child["UUID"] = mChildren[i]; children.emplace_back(child); } @@ -173,6 +236,7 @@ namespace lunarium #if !BUILD_NO_EDITOR // Only does this when this is an editor build mUUID = node["UUID"].get(); + mName = node["name"].get(); // TODO: Load components auto& components = node["components"]; @@ -255,6 +319,12 @@ namespace lunarium AddComponent(Color, Size); } + if ("ParentEntityComponent" == comp_type_name) + { + LUUID parent = comp["UUID"].get(); + AddComponent(parent); + } + // TODO: ADD CODE TO DESERIALIZE ANY NEW COMPONENTS } @@ -266,8 +336,7 @@ namespace lunarium { auto& child = *iter; - Entity* ne = new Entity(mWorld); - ne->Deserialize(child).LogIfFailed(LogCategory::GAME_SYSTEM); + LUUID ne = child["UUID"].get(); mChildren.push_back(ne); } diff --git a/src/world/entity.h b/src/world/entity.h index 005dfb3..b5fdaa1 100644 --- a/src/world/entity.h +++ b/src/world/entity.h @@ -65,9 +65,18 @@ namespace lunarium // TODO: : public JSONSerializable } LUUID GetUUID() const; + std::string GetName() const; + void SetName(std::string name); bool HasChildren() const; - void AddChild(Entity* pChild); + void AddChild(LUUID child); + void RemoveChild(LUUID child); + const std::vector& GetChildren() const; + + bool HasParent() const; + LUUID GetParent() const; + void SetParent(LUUID parent); + void ClearParent(); // Serializing [[nodiscard]] virtual OpRes Serialize(nlohmann::ordered_json& node); @@ -77,9 +86,12 @@ namespace lunarium // TODO: : public JSONSerializable private: LUUID mUUID; + LUUID mParent; + bool mParentSet; + std::string mName; entt::entity mHandle; World& mWorld; // The world the entity is contained within - std::vector mChildren; + std::vector mChildren; private: void Init(); diff --git a/src/world/world.cpp b/src/world/world.cpp index 2b74c19..fa9fb08 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -65,6 +65,8 @@ namespace lunarium // case RunMode::EDITOR: RenderEditor(pGraphics); break; // } + // TODO: Modify to handle parent/child rendering + // Draw the Renderables that also have a transform // Code from: @@ -180,6 +182,7 @@ namespace lunarium new_ent->Deserialize(ent).LogIfFailed(LogCategory::GAME_SYSTEM); mEntities.push_back(new_ent); + mEntitiesByUUID[new_ent->GetUUID()] = new_ent; } #endif diff --git a/test_data/engine_state.json b/test_data/engine_state.json index a338efe..5004d9f 100644 --- a/test_data/engine_state.json +++ b/test_data/engine_state.json @@ -2,7 +2,7 @@ "State": { "DataDirectory": "data/", - "Mode": "test", + "Mode": "editor", "Display": {