Explorar el Código

runtime view: support for excluded components (close #512)

Michele Caini hace 5 años
padre
commit
8c002b67d5
Se han modificado 5 ficheros con 84 adiciones y 49 borrados
  1. 4 0
      TODO
  2. 11 6
      docs/md/entity.md
  3. 23 8
      src/entt/entity/registry.hpp
  4. 15 27
      src/entt/entity/runtime_view.hpp
  5. 31 8
      test/entt/entity/runtime_view.cpp

+ 4 - 0
TODO

@@ -20,3 +20,7 @@ Next:
 * WIP: snapshot rework
  - snapshot: support for range-based archives
  - update documentation to describe alternatives
+
+* WIP:
+ - custom pools.
+ - the Perfect Model.

+ 11 - 6
docs/md/entity.md

@@ -1258,7 +1258,7 @@ going to return and to know whether it's empty or not. It's also possible to ask
 a runtime view if it contains a given entity.<br/>
 Refer to the inline documentation for all the details.
 
-Runtime view are extremely cheap to construct and should not be stored around in
+Runtime views are pretty cheap to construct and should not be stored around in
 any case. They should be used immediately after creation and then they should be
 thrown away. The reasons for this go far beyond the scope of this document.<br/>
 To iterate a runtime view, either use it in a range-for loop:
@@ -1289,13 +1289,18 @@ registry.runtime_view(std::cbegin(types), std::cend(types)).each([](auto entity)
 });
 ```
 
-Performance are exactly the same in both cases.
+Performance are exactly the same in both cases.<br/>
+Filtering entities by components is also supported for this kind of views:
+
+```cpp
+entt::component components[] = { entt::type_info<position>::id() };
+entt::component filter[] = { entt::type_info<velocity>::id() };
+auto view = registry.runtime_view(std::cbegin(components), std::cend(components), std::cbegin(filter), std::cend(filter));
+```
 
 **Note**: runtime views are meant for all those cases where users don't know at
-compile-time what components to use to iterate entities. This is particularly
-well suited to plugin systems and mods in general. Where possible, don't use
-runtime views, as their performance are slightly inferior to those of the other
-views.
+compile-time what components to _use_ to iterate entities. If possible, don't
+use runtime views as their performance are inferior to those of the other views.
 
 ## Groups
 

+ 23 - 8
src/entt/entity/registry.hpp

@@ -1218,21 +1218,36 @@ public:
      * some external inputs and don't know at compile-time what are the required
      * components.
      *
-     * @tparam It Type of input iterator.
-     * @param first An iterator to the first element of the range of components.
-     * @param last An iterator past the last element of the range of components.
+     * @tparam ItComp Type of input iterator for the components to use to
+     * construct the view.
+     * @tparam ItExcl Type of input iterator for the components to use to filter
+     * the view.
+     * @param first An iterator to the first element of the range of components
+     * to use to construct the view.
+     * @param last An iterator past the last element of the range of components
+     * to use to construct the view.
+     * @param from An iterator to the first element of the range of components
+     * to use to filter the view.
+     * @param to An iterator past the last element of the range of components to
+     * use to filter the view.
      * @return A newly created runtime view.
      */
-    template<typename It>
-    [[nodiscard]] entt::basic_runtime_view<Entity> runtime_view(It first, It last) const {
-        std::vector<const sparse_set<Entity> *> selected(std::distance(first, last));
+    template<typename ItComp, typename ItExcl = id_type *>
+    [[nodiscard]] entt::basic_runtime_view<Entity> runtime_view(ItComp first, ItComp last, ItExcl from = {}, ItExcl to = {}) const {
+        std::vector<const sparse_set<Entity> *> component(std::distance(first, last));
+        std::vector<const sparse_set<Entity> *> exclude(std::distance(from, to));
+
+        std::transform(first, last, component.begin(), [this](const auto ctype) {
+            const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.type_id == ctype; });
+            return it == pools.cend() ? nullptr : it->pool.get();
+        });
 
-        std::transform(first, last, selected.begin(), [this](const auto ctype) {
+        std::transform(from, to, exclude.begin(), [this](const auto ctype) {
             const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.type_id == ctype; });
             return it == pools.cend() ? nullptr : it->pool.get();
         });
 
-        return { std::move(selected) };
+        return { std::move(component), std::move(exclude) };
     }
 
     /**

+ 15 - 27
src/entt/entity/runtime_view.hpp

@@ -63,10 +63,9 @@ class basic_runtime_view {
     class view_iterator final {
         friend class basic_runtime_view<Entity>;
 
-        using direct_type = std::vector<const sparse_set<Entity> *>;
-
-        view_iterator(const direct_type &all, underlying_iterator curr) ENTT_NOEXCEPT
-            : pools{&all},
+        view_iterator(const std::vector<const sparse_set<Entity> *> &cpools, const std::vector<const sparse_set<Entity> *> &exclude, underlying_iterator curr) ENTT_NOEXCEPT
+            : pools{&cpools},
+              filter{&exclude},
               it{curr}
         {
             if(it != (*pools)[0]->end() && !valid()) {
@@ -75,9 +74,8 @@ class basic_runtime_view {
         }
 
         [[nodiscard]] bool valid() const {
-            return std::all_of(pools->begin()++, pools->end(), [entt = *it](const auto *curr) {
-                return curr->contains(entt);
-            });
+            return std::all_of(pools->begin()++, pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); })
+                    && std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); });
         }
 
     public:
@@ -126,12 +124,14 @@ class basic_runtime_view {
         }
 
     private:
-        const direct_type *pools;
+        const std::vector<const sparse_set<Entity> *> *pools;
+        const std::vector<const sparse_set<Entity> *> *filter;
         underlying_iterator it;
     };
 
-    basic_runtime_view(std::vector<const sparse_set<Entity> *> others) ENTT_NOEXCEPT
-        : pools{std::move(others)}
+    basic_runtime_view(std::vector<const sparse_set<Entity> *> cpools, std::vector<const sparse_set<Entity> *> exclude) ENTT_NOEXCEPT
+        : pools{std::move(cpools)},
+          filter{std::move(exclude)}
     {
         const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
             return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size());
@@ -184,13 +184,7 @@ public:
      * @return An iterator to the first entity that has the given components.
      */
     [[nodiscard]] iterator begin() const {
-        iterator it{};
-
-        if(valid()) {
-            it = { pools, pools[0]->begin() };
-        }
-
-        return it;
+        return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{};
     }
 
     /**
@@ -209,13 +203,7 @@ public:
      * given components.
      */
     [[nodiscard]] iterator end() const {
-        iterator it{};
-
-        if(valid()) {
-            it = { pools, pools[0]->end() };
-        }
-
-        return it;
+        return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{};
     }
 
     /**
@@ -224,9 +212,8 @@ public:
      * @return True if the view contains the given entity, false otherwise.
      */
     [[nodiscard]] bool contains(const entity_type entt) const {
-        return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *view) {
-            return view->find(entt) != view->end();
-        });
+        return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
+                && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
     }
 
     /**
@@ -253,6 +240,7 @@ public:
 
 private:
     std::vector<const sparse_set<Entity> *> pools;
+    std::vector<const sparse_set<Entity> *> filter;
 };
 
 

+ 31 - 8
test/entt/entity/runtime_view.cpp

@@ -12,7 +12,7 @@ TEST(RuntimeView, Functionalities) {
     registry.reserve<int>(0);
     registry.reserve<char>(0);
 
-    ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
+    entt::id_type types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
     ASSERT_TRUE(view.empty());
@@ -55,7 +55,7 @@ TEST(RuntimeView, Iterator) {
     registry.emplace<int>(entity);
     registry.emplace<char>(entity);
 
-    ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
+    entt::id_type types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     using iterator = typename decltype(view)::iterator;
 
@@ -91,7 +91,7 @@ TEST(RuntimeView, Contains) {
 
     registry.destroy(e0);
 
-    ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
+    entt::id_type types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
     ASSERT_FALSE(view.contains(e0));
@@ -110,7 +110,7 @@ TEST(RuntimeView, Empty) {
     registry.emplace<char>(e1);
     registry.emplace<float>(e1);
 
-    ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id(), entt::type_info<float>::id() };
+    entt::id_type types[] = { entt::type_info<int>::id(), entt::type_info<char>::id(), entt::type_info<float>::id() };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
     view.each([](auto) { FAIL(); });
@@ -130,7 +130,7 @@ TEST(RuntimeView, Each) {
     registry.emplace<int>(e1);
     registry.emplace<char>(e1);
 
-    ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
+    entt::id_type types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     std::size_t cnt = 0;
 
@@ -152,7 +152,7 @@ TEST(RuntimeView, EachWithHoles) {
     registry.emplace<int>(e0, 0);
     registry.emplace<int>(e2, 2);
 
-    ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
+    entt::id_type types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
     view.each([e0](auto entity) {
@@ -166,7 +166,7 @@ TEST(RuntimeView, MissingPool) {
     const auto e0 = registry.create();
     registry.emplace<int>(e0);
 
-    ENTT_ID_TYPE types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
+    entt::id_type types[] = { entt::type_info<int>::id(), entt::type_info<char>::id() };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
     ASSERT_TRUE(view.empty());
@@ -189,7 +189,7 @@ TEST(RuntimeView, EmptyRange) {
     const auto e0 = registry.create();
     registry.emplace<int>(e0);
 
-    const ENTT_ID_TYPE *ptr = nullptr;
+    const entt::id_type *ptr = nullptr;
     auto view = registry.runtime_view(ptr, ptr);
 
     ASSERT_TRUE(view.empty());
@@ -200,3 +200,26 @@ TEST(RuntimeView, EmptyRange) {
 
     ASSERT_EQ((std::find(view.begin(), view.end(), e0)), view.end());
 }
+
+TEST(RuntimeView, ExcludedComponents) {
+    entt::registry registry;
+
+    const auto e0 = registry.create();
+    registry.emplace<int>(e0);
+
+    const auto e1 = registry.create();
+    registry.emplace<int>(e1);
+    registry.emplace<char>(e1);
+
+    entt::id_type components[] = { entt::type_info<int>::id() };
+    entt::id_type filter[] = { entt::type_info<char>::id(), entt::type_info<double>::id() };
+    auto view = registry.runtime_view(std::begin(components), std::end(components), std::begin(filter), std::end(filter));
+
+    ASSERT_FALSE(view.empty());
+    ASSERT_TRUE(view.contains(e0));
+    ASSERT_FALSE(view.contains(e1));
+
+    view.each([e0](auto entity) {
+        ASSERT_EQ(e0, entity);
+    });
+}