Peoplemon  0.1.0
Peoplemon 3 game source documentation
ConversationTreeComponent.cpp
Go to the documentation of this file.
2 
3 #include <BLIB/Engine.hpp>
5 #include <Core/Properties.hpp>
7 #include <cmath>
8 
9 namespace editor
10 {
11 namespace component
12 {
13 namespace rdr
14 {
15 namespace
16 {
17 bool pipelinesCreated = false;
18 
19 using CustomBindings =
20  bl::rc::ds::Bindings<bl::rc::ds::GlobalUniformBuffer<ConversationTreeComponent::TreeCamera>>;
21 using CustomSet = bl::rc::ds::GenericDescriptorSetInstance<CustomBindings>;
22 using CustomSetFactory =
23  bl::rc::ds::GenericDescriptorSetFactory<CustomBindings, VK_SHADER_STAGE_VERTEX_BIT>;
24 
25 void ensurePipelinesCreated(bl::engine::Engine& engine) {
26  if (!pipelinesCreated) {
27  pipelinesCreated = true;
28 
29  auto& cache = engine.renderer().pipelineCache();
31  bl::rc::vk::PipelineParameters()
32  .withPrimitiveType(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
33  .withSimpleDepthStencil(true)
34  .addDescriptorSet<bl::rc::ds::TexturePoolFactory>()
35  .addDescriptorSet<bl::rc::ds::Scene2DFactory>()
36  .addDescriptorSet<bl::rc::ds::Object2DFactory>()
37  .addDescriptorSet<CustomSetFactory>()
38  .withShaders("Resources/Shaders/Editor/conversationTree.vert.spv",
39  bl::rc::Config::ShaderIds::Fragment2DUnlit)
40  .build());
42  bl::rc::vk::PipelineParameters()
43  .withPrimitiveType(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
44  .withSimpleDepthStencil(true)
45  .addDescriptorSet<bl::rc::ds::TexturePoolFactory>()
46  .addDescriptorSet<bl::rc::ds::Scene2DFactory>()
47  .addDescriptorSet<bl::rc::ds::Object2DFactory>()
48  .addDescriptorSet<CustomSetFactory>()
49  .withShaders("Resources/Shaders/Editor/conversationTree.vert.spv",
50  bl::rc::Config::ShaderIds::TextFragment)
51  .build());
52  }
53 }
54 
55 const sf::Color NodeColor(80, 90, 230);
56 const sf::Color TerminatorColor(230, 70, 120);
57 const sf::Color SelectedNodeColor(190, 190, 70);
58 const sf::Color DownArrowColor(30, 220, 65);
59 const sf::Color UpArrowColor(230, 90, 40);
60 
61 constexpr float ArrowWidth = 24.f;
62 constexpr float ArrowDepth = 18.f;
63 
64 } // namespace
65 
67 : Component(HighlightState::IgnoresMouse)
68 , enginePtr(nullptr)
69 , currentOverlay(nullptr)
70 , lastTreeVersion(std::numeric_limits<unsigned int>::max())
71 , uniform(nullptr) {}
72 
73 void ConversationTreeComponent::setVisible(bool visible) { background.setHidden(!visible); }
74 
75 void ConversationTreeComponent::onElementUpdated() {
76  auto& tree = getOwnerAs<ConversationTree>();
77 
78  // Update nodes, arrows, and text if changed
79  if (lastTreeVersion != tree.getTreeVersion()) {
80  lastTreeVersion = tree.getTreeVersion();
81 
82  // circles
83  nodeCircles.resize(tree.getNodes().size());
84  nodeLabels.resize(tree.getNodes().size());
85  unsigned int i = 0;
86  for (auto& src : tree.getNodes()) {
87  auto& circle = nodeCircles[i];
88  if (!circle.isCreated()) {
89  circle.create(*enginePtr, shapeBatch, ConversationTree::NodeRadius);
90  circle.getLocalTransform().setOrigin(ConversationTree::NodeRadius,
92  circle.setFillColor(NodeColor);
93  }
94  circle.getLocalTransform().setPosition(src.center);
95 
96  auto& lbl = nodeLabels[i];
97  if (!lbl) {
98  lbl = std::make_unique<bl::gfx::Text>();
99  lbl->create(*enginePtr,
101  src.label,
102  18,
103  sf::Color::Black,
104  sf::Text::Style::Bold);
105  lbl->setParent(shapeBatch);
106  if (currentOverlay) {
107  lbl->addToSceneWithCustomPipeline(
108  currentOverlay,
109  bl::rc::UpdateSpeed::Static,
111  }
112  }
113  else { lbl->getSection().setString(src.label); }
114  lbl->commit();
115 
116  // reposition text vertices similar to how ShapeBatch works
117  bl::com::Transform2D transform;
118  transform.setOrigin(lbl->getLocalSize() * 0.5f);
119  transform.setPosition(src.center);
120  for (auto& v : lbl->component().vertices.vertices()) {
121  v.pos = transform.transformPoint(v.pos);
122  }
123  lbl->getTransform().setPosition(shapeBatch.getTransform().getLocalPosition());
124 
125  ++i;
126  }
127 
128  // arrows
129  constexpr float SrcSize = 25.f;
130  nodeArrows.resize(tree.getEdges().size(),
131  bl::gfx::BatchIcon(bl::gfx::BatchIcon::Type::Arrow, {SrcSize, SrcSize}));
132  i = 0;
133  for (auto& src : tree.getEdges()) {
134  auto& arrow = nodeArrows[i];
135  ++i;
136 
137  if (!arrow.isCreated()) { arrow.create(*enginePtr, shapeBatch); }
138 
139  const auto& from = tree.getNodes()[src.from];
140  const auto& to = tree.getNodes()[src.to];
141 
142  const glm::vec2 pdiff = to.center - from.center;
143  const float gap = glm::length(pdiff) - ConversationTree::NodeRadius * 2.f + ArrowDepth;
144  const float angle = atan2f(pdiff.y, pdiff.x);
145  const float degrees = bl::math::radiansToDegrees(angle) + 90.f;
146  arrow.setSize({ArrowWidth, gap});
147  arrow.getLocalTransform().setOrigin(arrow.getSize() * 0.5f);
148  arrow.getLocalTransform().setPosition(from.center + pdiff * 0.5f);
149  arrow.getLocalTransform().setRotation(degrees);
150  arrow.setFillColor(src.to > src.from ? DownArrowColor : UpArrowColor);
151  }
152  }
153 
154  // Always update camera and colors
155  const auto* srcNode = &tree.getNodes()[0];
156  unsigned int i = 0;
157  for (auto& node : nodeCircles) {
158  sf::Color col = srcNode->terminator ? TerminatorColor : NodeColor;
159  if (tree.shouldHighlightSelectedNode() && i == tree.getSelectedNode()) {
160  col = SelectedNodeColor;
161  }
162  const glm::vec4 gcol = bl::sfcol(col);
163  if (gcol != node.getFillColor()) { node.setFillColor(gcol); }
164 
165  ++i;
166  ++srcNode;
167  }
168 
169  if (uniform) {
170  uniform->center = tree.getCamCenter();
171  uniform->zoom = tree.getCamZoom();
172  uniform->acqSize.x = tree.getAcquisition().width;
173  uniform->acqSize.y = tree.getAcquisition().height;
174  }
175 }
176 
177 void ConversationTreeComponent::onRenderSettingChange() {}
178 
179 bl::ecs::Entity ConversationTreeComponent::getEntity() const { return background.entity(); }
180 
181 void ConversationTreeComponent::doCreate(bl::engine::Engine& engine, bl::gui::rdr::Renderer&) {
182  enginePtr = &engine;
183  ensurePipelinesCreated(engine);
184 
185  background.create(engine, {100.f, 100.f});
186  background.setFillColor(sf::Color::Cyan);
187  background.setOutlineColor(sf::Color::Black);
188  background.setOutlineThickness(-2.f);
189  background.getOverlayScaler().setScissorMode(bl::com::OverlayScaler::ScissorSelfConstrained);
190 
191  shapeBatch.create(engine, 2048);
192  shapeBatch.setParent(background);
193 }
194 
195 void ConversationTreeComponent::doSceneAdd(bl::rc::Overlay* overlay) {
196  currentOverlay = overlay;
197 
198  background.addToScene(overlay, bl::rc::UpdateSpeed::Static);
199  shapeBatch.addToSceneWithCustomPipeline(
200  overlay,
201  bl::rc::UpdateSpeed::Static,
203  for (auto& label : nodeLabels) {
204  label->addToSceneWithCustomPipeline(overlay,
205  bl::rc::UpdateSpeed::Static,
207  }
208 
209  uniform = &overlay->getDescriptorSet<CustomSet>().getBindingPayload<TreeCamera>();
210 }
211 
212 void ConversationTreeComponent::doSceneRemove() { background.removeFromScene(); }
213 
214 void ConversationTreeComponent::handleAcquisition() {
215  const auto& acq = getOwnerAs<ConversationTree>().getAcquisition();
216  background.setSize({acq.width, acq.height});
217  handleMove();
218  if (uniform) {
219  uniform->acqSize.x = acq.width;
220  uniform->acqSize.y = acq.height;
221  }
222 }
223 
224 void ConversationTreeComponent::handleMove() {
225  const auto pos = getOwnerAs<ConversationTree>().getLocalPosition();
226  background.getTransform().setPosition(pos.x, pos.y);
227 }
228 
229 } // namespace rdr
230 } // namespace component
231 } // namespace editor
All classes and functionality used for implementing the game editor.
Definition: Tile.hpp:11
static const sf::VulkanFont & MenuFont()
Definition: Properties.cpp:363
static constexpr std::uint32_t EditorConversationTreeTextPipelineId
Definition: Properties.hpp:146
static constexpr std::uint32_t EditorConversationTreeShapePipelineId
Definition: Properties.hpp:145