diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index a784e3317..02e14b75b 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -732,7 +732,7 @@ void EditorComponent::Load() clearButton.Create("Clear World"); - clearButton.SetTooltip("Delete every model from the scene"); + clearButton.SetTooltip("Delete everything from the scene. This operation is not undoable!"); clearButton.SetColor(wi::Color(255, 173, 43, 180), wi::gui::WIDGETSTATE::IDLE); clearButton.SetColor(wi::Color(255, 235, 173, 255), wi::gui::WIDGETSTATE::FOCUS); clearButton.OnClick([&](wi::gui::EventArgs args) { @@ -757,6 +757,9 @@ void EditorComponent::Load() nameWnd.SetEntity(INVALID_ENTITY); RefreshSceneGraphView(); + + history.clear(); + historyPos = -1; }); GetGUI().AddWidget(&clearButton); @@ -848,6 +851,19 @@ void EditorComponent::Load() sceneGraphView.Create("Scene graph view"); sceneGraphView.OnSelect([this](wi::gui::EventArgs args) { + wi::Archive& archive = AdvanceHistory(); + archive << HISTORYOP_SELECTION; + // record PREVIOUS selection state... + archive << translator.selected.size(); + for (auto& x : translator.selected) + { + archive << x.entity; + archive << x.position; + archive << x.normal; + archive << x.subsetIndex; + archive << x.distance; + } + translator.selected.clear(); for (int i = 0; i < sceneGraphView.GetItemCount(); ++i) @@ -861,6 +877,17 @@ void EditorComponent::Load() } } + // record NEW selection state... + archive << translator.selected.size(); + for (auto& x : translator.selected) + { + archive << x.entity; + archive << x.position; + archive << x.normal; + archive << x.subsetIndex; + archive << x.distance; + } + }); sceneGraphView.SetColor(wi::Color(100, 100, 100, 100), wi::gui::IDLE); GetGUI().AddWidget(&sceneGraphView); @@ -1443,11 +1470,12 @@ void EditorComponent::Update(float dt) { auto prevSel = translator.selected; + EntitySerializer seri; clipboard.SetReadModeAndResetPos(false); clipboard << prevSel.size(); for (auto& x : prevSel) { - scene.Entity_Serialize(clipboard, x.entity); + scene.Entity_Serialize(clipboard, seri, x.entity, Scene::EntitySerializeFlags::RECURSIVE); } } // Paste @@ -1456,13 +1484,14 @@ void EditorComponent::Update(float dt) auto prevSel = translator.selected; translator.selected.clear(); + EntitySerializer seri; clipboard.SetReadModeAndResetPos(true); size_t count; clipboard >> count; for (size_t i = 0; i < count; ++i) { wi::scene::PickResult picked; - picked.entity = scene.Entity_Serialize(clipboard); + picked.entity = scene.Entity_Serialize(clipboard, seri, INVALID_ENTITY, Scene::EntitySerializeFlags::RECURSIVE | Scene::EntitySerializeFlags::KEEP_INTERNAL_ENTITY_REFERENCES); AddSelected(picked); } @@ -1485,12 +1514,13 @@ void EditorComponent::Update(float dt) // Put Instances if (clipboard.IsOpen() && hovered.subsetIndex >= 0 && wi::input::Down(wi::input::KEYBOARD_BUTTON_LSHIFT) && wi::input::Press(wi::input::MOUSE_BUTTON_LEFT)) { + EntitySerializer seri; clipboard.SetReadModeAndResetPos(true); size_t count; clipboard >> count; for (size_t i = 0; i < count; ++i) { - Entity entity = scene.Entity_Serialize(clipboard); + Entity entity = scene.Entity_Serialize(clipboard, seri, INVALID_ENTITY, Scene::EntitySerializeFlags::RECURSIVE | Scene::EntitySerializeFlags::KEEP_INTERNAL_ENTITY_REFERENCES); TransformComponent* transform = scene.transforms.GetComponent(entity); if (transform != nullptr) { @@ -1532,9 +1562,10 @@ void EditorComponent::Update(float dt) { archive << x.entity; } + EntitySerializer seri; for (auto& x : translator.selected) { - scene.Entity_Serialize(archive, x.entity); + scene.Entity_Serialize(archive, seri, x.entity); } for (auto& x : translator.selected) { @@ -2233,7 +2264,18 @@ void EditorComponent::PushToSceneGraphView(wi::ecs::Entity entity, int level) item.selected = IsSelected(entity); item.open = scenegraphview_opened_items.count(entity) != 0; const NameComponent* name = scene.names.GetComponent(entity); - item.name = name == nullptr ? std::to_string(entity) : name->name; + if (name == nullptr) + { + item.name = "[no_name] " + std::to_string(entity); + } + else if(name->name.empty()) + { + item.name = "[name_empty] " + std::to_string(entity); + } + else + { + item.name = name->name; + } sceneGraphView.AddItem(item); scenegraphview_added_items.insert(entity); @@ -2427,9 +2469,11 @@ void EditorComponent::ConsumeHistoryOperation(bool undo) if (undo) { + EntitySerializer seri; + seri.allow_remap = false; for (size_t i = 0; i < count; ++i) { - scene.Entity_Serialize(archive); + scene.Entity_Serialize(archive, seri); } } else diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index ca79a2008..1c1b06c92 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -1983,14 +1983,31 @@ namespace wi::scene bounds = AABB::Merge(bounds, other.bounds); } - void Scene::Entity_Remove(Entity entity) + void Scene::Entity_Remove(Entity entity, bool recursive) { - Component_Detach(entity); // special case, this will also remove entity from hierarchy but also do more! + if (recursive) + { + wi::vector entities_to_remove; + for (size_t i = 0; i < hierarchy.GetCount(); ++i) + { + const HierarchyComponent& hier = hierarchy[i]; + if (hier.parentID == entity) + { + Entity child = hierarchy.GetEntity(i); + entities_to_remove.push_back(child); + } + } + for (auto& child : entities_to_remove) + { + Entity_Remove(child); + } + } names.Remove(entity); layers.Remove(entity); transforms.Remove(entity); prev_transforms.Remove(entity); + hierarchy.Remove(entity); materials.Remove(entity); meshes.Remove(entity); impostors.Remove(entity); @@ -2030,14 +2047,15 @@ namespace wi::scene Entity Scene::Entity_Duplicate(Entity entity) { wi::Archive archive; + EntitySerializer seri; // First write the root entity to staging area: archive.SetReadModeAndResetPos(false); - Entity_Serialize(archive, entity); + Entity_Serialize(archive, seri, entity, EntitySerializeFlags::RECURSIVE); // Then deserialize root: archive.SetReadModeAndResetPos(true); - Entity root = Entity_Serialize(archive); + Entity root = Entity_Serialize(archive, seri, INVALID_ENTITY, EntitySerializeFlags::RECURSIVE | EntitySerializeFlags::KEEP_INTERNAL_ENTITY_REFERENCES); return root; } @@ -2410,11 +2428,9 @@ namespace wi::scene if (channel.path == AnimationComponent::AnimationChannel::Path::WEIGHTS) { ObjectComponent* object = objects.GetComponent(channel.target); - assert(object != nullptr); if (object == nullptr) continue; target_mesh = meshes.GetComponent(object->meshID); - assert(target_mesh != nullptr); if (target_mesh == nullptr) continue; animation.morph_weights_temp.resize(target_mesh->targets.size()); @@ -2422,7 +2438,6 @@ namespace wi::scene else { target_transform = transforms.GetComponent(channel.target); - assert(target_transform != nullptr); if (target_transform == nullptr) continue; transform = *target_transform; @@ -2936,10 +2951,12 @@ namespace wi::scene int boneIndex = 0; for (Entity boneEntity : armature.boneCollection) { - const TransformComponent& bone = *transforms.GetComponent(boneEntity); + const TransformComponent* bone = transforms.GetComponent(boneEntity); + if (bone == nullptr) + continue; XMMATRIX B = XMLoadFloat4x4(&armature.inverseBindMatrices[boneIndex]); - XMMATRIX W = XMLoadFloat4x4(&bone.world); + XMMATRIX W = XMLoadFloat4x4(&bone->world); XMMATRIX M = B * W * R; XMFLOAT4X4 mat; @@ -2947,7 +2964,7 @@ namespace wi::scene armature.boneData[boneIndex++].Create(mat); const float bone_radius = 1; - XMFLOAT3 bonepos = bone.GetPosition(); + XMFLOAT3 bonepos = bone->GetPosition(); AABB boneAABB; boneAABB.createFromHalfWidth(bonepos, XMFLOAT3(bone_radius, bone_radius, bone_radius)); _min = wi::math::Min(_min, boneAABB._min); diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 9eba85692..b87ca02f4 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -1376,16 +1376,33 @@ namespace wi::scene // The contents of the other scene will be lost (and moved to this)! void Merge(Scene& other); - // Removes a specific entity from the scene (if it exists): - void Entity_Remove(wi::ecs::Entity entity); + // Removes (deletes) a specific entity from the scene (if it exists): + // recursive : also removes children if true + void Entity_Remove(wi::ecs::Entity entity, bool recursive = true); // Finds the first entity by the name (if it exists, otherwise returns INVALID_ENTITY): wi::ecs::Entity Entity_FindByName(const std::string& name); // Duplicates all of an entity's components and creates a new entity with them (recursively keeps hierarchy): wi::ecs::Entity Entity_Duplicate(wi::ecs::Entity entity); + + enum class EntitySerializeFlags + { + NONE = 0, + RECURSIVE = 1 << 0, // children entities will be also serialized + KEEP_INTERNAL_ENTITY_REFERENCES = 1 << 1, // entity handles inside components will be kept intact, they won't use remapping of wi::ecs::EntitySerializer + }; // Serializes entity and all of its components to archive: + // archive : archive used for serializing data + // seri : serializer state for entity component system + // entity : if archive is in write mode, this is the entity to serialize. If archive is in read mode, it should be INVALID_ENTITY + // flags : specify options as EntitySerializeFlags bits to control internal behaviour + // // Returns either the new entity that was read, or the original entity that was written - // This serialization is recursive and serializes entity hierarchy as well - wi::ecs::Entity Entity_Serialize(wi::Archive& archive, wi::ecs::Entity entity = wi::ecs::INVALID_ENTITY); + wi::ecs::Entity Entity_Serialize( + wi::Archive& archive, + wi::ecs::EntitySerializer& seri, + wi::ecs::Entity entity = wi::ecs::INVALID_ENTITY, + EntitySerializeFlags flags = EntitySerializeFlags::RECURSIVE + ); wi::ecs::Entity Entity_CreateMaterial( const std::string& name @@ -1538,3 +1555,8 @@ namespace wi::scene SceneIntersectSphereResult SceneIntersectCapsule(const wi::primitive::Capsule& capsule, uint32_t renderTypeMask = wi::enums::RENDERTYPE_OPAQUE, uint32_t layerMask = ~0, const Scene& scene = GetScene()); } + +template<> +struct enable_bitmask_operators { + static const bool enable = true; +}; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 8936bc9a7..abf728a33 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -1362,15 +1362,20 @@ namespace wi::scene wi::backlog::post("Scene serialize took " + std::to_string(timer.elapsed_seconds()) + " sec"); } - Entity Scene::Entity_Serialize(wi::Archive& archive, Entity entity) + Entity Scene::Entity_Serialize( + wi::Archive& archive, + EntitySerializer& seri, + Entity entity, + EntitySerializeFlags flags + ) { - EntitySerializer seri; - SerializeEntity(archive, entity, seri); - // From this point, we will not remap the entities, - // to retain internal entity references inside components: - seri.allow_remap = false; + bool restore_remap = seri.allow_remap; + if (has_flag(flags, EntitySerializeFlags::KEEP_INTERNAL_ENTITY_REFERENCES)) + { + seri.allow_remap = false; + } if (archive.IsReadMode()) { @@ -1641,14 +1646,15 @@ namespace wi::scene } } - if (archive.GetVersion() >= 72) + if (archive.GetVersion() >= 72 && has_flag(flags, EntitySerializeFlags::RECURSIVE)) { // serialize children: + seri.allow_remap = restore_remap; size_t childCount = 0; archive >> childCount; for (size_t i = 0; i < childCount; ++i) { - Entity child = Entity_Serialize(archive); + Entity child = Entity_Serialize(archive, seri, INVALID_ENTITY, flags); if (child != INVALID_ENTITY) { HierarchyComponent* hier = hierarchy.GetComponent(child); @@ -2016,9 +2022,10 @@ namespace wi::scene } } - if (archive.GetVersion() >= 72) + if (archive.GetVersion() >= 72 && has_flag(flags, EntitySerializeFlags::RECURSIVE)) { // Recursive serialization for all children: + seri.allow_remap = restore_remap; wi::vector children; for (size_t i = 0; i < hierarchy.GetCount(); ++i) { @@ -2032,11 +2039,12 @@ namespace wi::scene archive << children.size(); for (Entity child : children) { - Entity_Serialize(archive, child); + Entity_Serialize(archive, seri, child, flags); } } } + seri.allow_remap = restore_remap; return entity; } diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index f9936ad17..4bd6e8811 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 60; // minor bug fixes, alterations, refactors, updates - const int revision = 25; + const int revision = 26; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);