From a8d0606127b45e5dc444ab3d14228c412797cff3 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Mon, 13 Feb 2023 15:02:01 -0500 Subject: [PATCH] WIP (broken): Refactor components --- entt-question.md | 71 ++++++++++++++++++++++++++++++++++++++++++ vis/src/components.hpp | 58 +++++++++++++--------------------- vis/src/systems.hpp | 36 ++++++++++++--------- 3 files changed, 115 insertions(+), 50 deletions(-) create mode 100644 entt-question.md diff --git a/entt-question.md b/entt-question.md new file mode 100644 index 0000000..50f4784 --- /dev/null +++ b/entt-question.md @@ -0,0 +1,71 @@ +I have some "composite" objects that I'm not sure how best to map into an ECS. Each composite object has multiple "parts" with various properties. But the parts of a given composite object are intrinsically linked: they transform together and use the same buffers during rendering. + +Note that I'm still pretty early in adopting `entt` for this project, so I'm not particularly tied to any of the architecture here. The only constraint is that, for optimization reasons, I need each composite object to get _one_ buffer on GPU, and each part owns some interval within its parent's buffer. This nested scheme seems the most straightforward way to do that. + +--- + +My first attempt has only one entity for each composite object, with a `Parts` component that holds an `std::vector`. + +Rendering code looks something like this. In reality I save the draw commands in a separate component and re-use those on each frame. + +```c++ +auto view = registry.view(); + +for (auto [entity, parts]: view.each()) { + std::vector commands; + + for (auto part: parts.parts) { + // assemble draw command + commands.emplace_back(...); + } + + // bind buffers for entity + // issue draw command +} +``` + +However it seems appropriate to give each part its own entity. My attempt for this was to switch to `std::vector` where each entity has a `Part` as a component. + +Rendering code then looks like: + +```c++ +auto view = registry.view(); + +for (auto [entity, parts]: view.each()) { + std::vector commands; + + for (auto part_entity: parts.parts) { + auto &part = registry.get(part_entity); + // assemble draw command + commands.emplace_back(...); + } + + // bind buffers for entity + // issue draw command with shared buffer +} +``` + +--- + +In my last attempt, I invert this so each `Part` holds only parent entity. The `Parts` component is removed. I can then use a `view` directly, but it's trickier to organize the draw calls since they must be grouped by the parent entity itself. + +```c++ +auto view = registry.view(); + +for (auto [entity, part]: view.each()) { + // assemble draw command + auto &commands = registry.get(part.parent); + commands.emplace_back(...); +} + +for (auto [entity, commands]: view +for (auto [entity, commands]: commands) { + // bind buffers for entity + auto &commands = registry.get(entity); + // issue draw commands +} +``` + +--- + +Is there any more sensible approach to this kind of many-to-one entity composition? Or some way to declare _multiple_ `Part` components on the same entity? It seems like I almost need some group-by kind of operation, but I don't see such on the registry/view docs. \ No newline at end of file diff --git a/vis/src/components.hpp b/vis/src/components.hpp index 9b991e2..0425d75 100644 --- a/vis/src/components.hpp +++ b/vis/src/components.hpp @@ -18,56 +18,46 @@ #include namespace vis { + using Color = Eigen::Vector; + template struct Structure { static constexpr auto Rank = R_; static constexpr auto Dim = D_; static constexpr auto Grade = G_; - using Affine = Eigen::Transform; + using AffineDf = Eigen::Transform; using VectorRf = Eigen::Vector; - using MatrixRXf = Eigen::Matrix; - using VectorDf = Eigen::Vector; - using MatrixDXf = Eigen::Matrix; - using VectorGf = Eigen::Vector; - using MatrixGXf = Eigen::Matrix; - - using ArrayRui = Eigen::Array; - using ArrayRXui = Eigen::Array; - using ArrayDui = Eigen::Array; - using ArrayDXui = Eigen::Array; - using ArrayGui = Eigen::Array; - using ArrayGXui = Eigen::Array; - - using Color = Eigen::Vector; // todo global typedef // todo cache and recompute cells/points on frame (only if changed) in a system. tc::Group group; VectorRf root; - Affine transform = Affine::Identity(); + AffineDf transform = AffineDf::Identity(); - struct Part { - GLuint first; - GLuint count; - Color color = Color::Ones(); - bool enabled = true; - }; - - std::vector parts{}; - - explicit Structure( - tc::Group group, - VectorRf root - ) : - group(group), - root(root), - transform(Affine::Identity()) { + explicit Structure(tc::Group const &group, VectorRf root) : + group(group), root(root), transform(AffineDf::Identity()) { } }; + template + struct Part { + using Str = Str_; + + entt::entity parent; + + GLuint first; + GLuint count; + Color color = Color::Ones(); + bool enabled = true; + }; + + struct Command { + unsigned int count, instanceCount, first, baseInstance; + }; + template struct VBOs { using Str = Str_; @@ -81,10 +71,6 @@ namespace vis { Eigen::Vector4f translation; }; - struct Command { - unsigned int count, instanceCount, first, baseInstance; - }; - cgl::Buffer vertices; cgl::Buffer colors; cgl::Buffer indices; diff --git a/vis/src/systems.hpp b/vis/src/systems.hpp index 5bf06df..41a14fe 100644 --- a/vis/src/systems.hpp +++ b/vis/src/systems.hpp @@ -21,39 +21,42 @@ namespace vis { template void upload_structure(entt::registry ®istry) { + { + auto parts = registry.view>(); + registry.destroy(parts.begin(), parts.end()); + } + auto view = registry.view>(); for (auto [entity, structure, vbos]: view.each()) { Points points(structure.group, structure.root); Hull hull(structure.group); - registry.destroy(structure.parts.begin(), structure.parts.end()); - structure.parts.clear(); + auto &&vertices = points.verts.colwise(); + auto &&indices = hull.inds.colwise(); + + vbos.vertices.put(vertices.begin(), vertices.end()); + vbos.indices.put(indices.begin(), indices.end()); for (const auto &tiling: hull.tilings) { auto part_entity = registry.create(); - registry.emplace( + registry.emplace>( part_entity, + entity, tiling.first, tiling.count ); - structure.parts.push_back(part_entity); } - - vbos.vertices.put( - points.verts.colwise().begin(), - points.verts.colwise().end() - ); - vbos.indices.put( - hull.inds.colwise().begin(), - hull.inds.colwise().end() - ); } } template void upload_uniforms(entt::registry ®istry) { - auto view = registry.view>(); + auto view = registry.view>(); + + for (auto [entity, part]: view.each()) { + auto &vbos = registry.get>(part.parent); + } for (auto [entity, structure, vbos]: view.each()) { std::vector colors; @@ -73,6 +76,11 @@ namespace vis { template void upload_commands(entt::registry ®istry) { + auto view = registry.view>(); + for (auto [entity, part]: view.each()) { + Command comm(part.count, 1, part.first, ) + } + auto view = registry.view>(); for (auto [entity, structure, vbos]: view.each()) {