Cogs.Core
EntityInspector.cpp
1#include "EntityInspector.h"
2
3#include "Context.h"
4#include "Engine.h"
5#include "EntityStore.h"
6#include "Types.h"
7
8#include "Editor/IEditor.h"
9
10#include "InspectorGuiHelper.h"
11#include "Inspectors.h"
12
13#include "Components/Core/PropertiesComponent.h"
14#include "Components/Core/SceneComponent.h"
15
16#include "Resources/Resources.h"
17#include "Resources/TextureManager.h"
18#include "Renderer/Renderer.h"
19#include "Renderer/RenderTexture.h"
20#include "Resources/MaterialInstance.h"
21#include "Resources/Model.h"
22#include "Resources/Mesh.h"
23
24#include "Foundation/ComponentModel/Entity.h"
25#include "Foundation/Reflection/TypeDatabase.h"
26
27#include "imgui.h"
28
29#include <glm/vec2.hpp>
30#include <glm/vec3.hpp>
31#include <glm/vec4.hpp>
32#include <glm/mat4x4.hpp>
33#include <glm/ext/quaternion_float.hpp>
34
35#include <algorithm>
36#include <sstream>
37#include <iomanip>
38#include <array>
39#include <bit>
40#include <cstdio>
41
42using namespace Cogs::Reflection;
43using namespace Cogs::ComponentModel;
44
45
46void Cogs::Core::showCollapsableEntityInspector(Cogs::Core::Context* context, const ComponentModel::Entity* entity, std::string_view entityNamePattern, std::string_view componentNamePattern, bool showBorder)
47{
48 if (Cogs::Core::findHierarchyWithMatch(*entity, entityNamePattern, componentNamePattern)) {
49 std::array<char, 128> header = {};
50 std::snprintf(header.data(), header.size(), "%s Id: %zu", entity->getName().c_str(), entity->getId());
51
52 ImGui::PushID(entity);
53
54 if (ImGui::TreeNodeEx(header.data(), ImGuiTreeNodeFlags_NoAutoOpenOnLog | (showBorder ? ImGuiTreeNodeFlags_Framed : 0))) {
55 ScopedIndent si;
56 showEntityInspector(context, entity, entityNamePattern, componentNamePattern, showBorder);
57
58 ImGui::TreePop();
59 }
60
61 ImGui::PopID();
62 }
63}
64
65void Cogs::Core::showEntities(Context * context, std::span<const EntityPtr> entities, std::string_view entityNamePattern, std::string_view componentNamePattern)
66{
67 for (const EntityPtr & e : entities) {
68 showCollapsableEntityInspector(context, e.get(), entityNamePattern, componentNamePattern);
69 }
70}
71
72void Cogs::Core::showEntityInspector(Context* context, const ComponentModel::Entity* entity, std::string_view entityNamePattern, std::string_view componentNamePattern, bool showBorder)
73{
74 ImGui::Text("Id: %zd", entity->getId());
75 ImGui::SameLine();
76 const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
77 static std::array<char, 65> buffer;
78 ImGui::SameLine(0.0f, spacing);
79 constexpr int widthInputText = 120;
80 ImGui::PushItemWidth(widthInputText);
81 ImGui::Text("Filter: ");
82 ImGui::SameLine(0.0f, spacing);
83 if (ImGui::InputText(" ", buffer.data(), buffer.size(), ImGuiInputTextFlags_None)) {
84 ImguiRenderer* guiRenderer = context->renderer->getGuiRenderer();
85 if (guiRenderer) {
86 guiRenderer->addTextToGlyphBuilder(buffer.data());
87 }
88 }
89 ImGui::PopItemWidth();
90
91 std::string_view findPattern = buffer.data();
92
93 for (const Cogs::ComponentModel::ComponentHandle& c : entity->getComponents()) {
94 Cogs::ComponentModel::Component* component = c.resolve();
95
96 bool showComponent = (bool)component;
97 if(showComponent && !findPattern.empty()) {
98 showComponent = containsInvariantCase(component->getType().getName().c_str(), findPattern);
99 }
100
101 if(showComponent) {
102 showComponentInspector(context, component, entityNamePattern, componentNamePattern, showBorder);
103 }
104 }
105}
106
107void Cogs::Core::showComponentInspector(Context* context, ComponentModel::Component* component, std::string_view entityNamePattern, std::string_view componentNamePattern, bool showBorder)
108{
109 const Cogs::Reflection::Type* componentType = &component->getType();
110
111 if (ImGui::TreeNodeEx(componentType->getName().getName().c_str(), ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog | (showBorder ? ImGuiTreeNodeFlags_Framed : 0))) {
112 ImGui::PushID(component);
113 ImGui::Text(" ");
114 const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
115 static std::array<char, 65> buffer;
116 ImGui::SameLine(0.0f, spacing);
117 constexpr int widthInputText = 120;
118 ImGui::PushItemWidth(widthInputText);
119 ImGui::Text("Filter: ");
120 ImGui::SameLine(0.0f, spacing);
121 if (ImGui::InputText(" ", buffer.data(), buffer.size(), ImGuiInputTextFlags_None)) {
122 ImguiRenderer* guiRenderer = context->renderer->getGuiRenderer();
123 if (guiRenderer) {
124 guiRenderer->addTextToGlyphBuilder(buffer.data());
125 }
126 }
127 ImGui::PopItemWidth();
128 auto updateGui = componentType->getMethod("updateGui");
129 if (updateGui) {
130 updateGui->call(component);
131 ImGui::PopID();
132 return;
133 }
134
135 std::string_view findPattern = buffer.data();
136
137 // Show fields
138 ScopedIndent si_type;
139 size_t numFields = componentType->getNumHierarchyFields();
140
141 for (FieldId fieldId = 0; fieldId < numFields; ++fieldId) {
142 bool showField = true;
143 if(!findPattern.empty()) {
144 showField = containsInvariantCase(componentType->getField(fieldId)->getName().c_str(), findPattern);
145 }
146
147 if(showField) {
148 showFieldInspector(context, component, fieldId, entityNamePattern, componentNamePattern, showBorder);
149 }
150 }
151
152 ImGui::PopID();
153 }
154}
155
156void Cogs::Core::showFieldInspector(Context* context, ComponentModel::Component* component, FieldId fieldId, std::string_view entityNamePattern, std::string_view componentNamePattern, bool showBorder)
157{
158 const Cogs::Reflection::Type* componentType = &component->getType();
159
160 const Cogs::Reflection::Field* field = componentType->getField(fieldId);
161 const Cogs::Reflection::Name& fieldName = field->getName();
162
163 const std::string & fieldHeader = fieldName.getName();
164
165 const Cogs::Reflection::Type & fieldType = TypeDatabase::getType(field->getTypeId());
166
167 if (fieldType == TypeDatabase::getType<int32_t>()) {
168 auto fieldValue = field->getPtr<int32_t>(component);
169 const auto* range = field->get<ComponentModel::RangeAttribute<int>>();
170 if (range) {
171 const int32_t min = std::min(range->getMin(), *fieldValue);
172 const int32_t max = std::max(range->getMax(), *fieldValue);
173 if (ImGui::SliderInt(fieldHeader.c_str(), fieldValue, min, max)) {
174 component->setFieldChanged(fieldId);
175 }
176 }
177 else {
178 if (ImGui::DragInt(fieldHeader.c_str(), fieldValue)) {
179 component->setFieldChanged(fieldId);
180 }
181 }
182 }
183 else if (fieldType == TypeDatabase::getType<float>()) {
184 auto fieldValue = field->getPtr<float>(component);
185
186 const auto* range = field->get<ComponentModel::RangeAttribute<float>>();
187 if (range) {
188 const float min = std::min(range ? range->getMin() : 0.0f, *fieldValue);
189 const float max = std::max(range ? range->getMax() : 100.0f, *fieldValue);
190 const float stepValue = (max - min) / 100.f;
191
192 if (ImGui::DragFloat(fieldHeader.c_str(), fieldValue, stepValue, min, max)) {
193 component->setFieldChanged(fieldId);
194 }
195 }
196 else {
197 if (ImGui::DragFloat(fieldHeader.c_str(), fieldValue)) {
198 component->setFieldChanged(fieldId);
199 }
200 }
201 }
202 else if (fieldType == TypeDatabase::getType<glm::vec2>()) {
203 auto fieldValue = field->getPtr<glm::vec2>(component);
204
205 if (ImGui::DragFloat2(fieldHeader.c_str(), glm::value_ptr(*fieldValue))) {
206 component->setFieldChanged(fieldId);
207 }
208 }
209 else if (fieldType == TypeDatabase::getType<glm::vec3>()) {
210 auto fieldValue = field->getPtr<glm::vec3>(component);
211
212 if (ImGui::DragFloat3(fieldHeader.c_str(), glm::value_ptr(*fieldValue))) {
213 component->setFieldChanged(fieldId);
214 }
215 }
216 else if (fieldType == TypeDatabase::getType<glm::vec4>()) {
217 auto fieldValue = field->getPtr<glm::vec4>(component);
218
219 auto ca = field->get<ComponentModel::ColorAttribute>();
220
221 if (ca || (fieldName.getName().find("olor") != std::string::npos)) {
222 if (ImGui::ColorEdit4(fieldHeader.c_str(), glm::value_ptr(*fieldValue))) {
223 component->setFieldChanged(fieldId);
224 }
225 } else {
226 if (ImGui::DragFloat4(fieldHeader.c_str(), glm::value_ptr(*fieldValue))) {
227 component->setFieldChanged(fieldId);
228 }
229 }
230 }
231 else if (fieldType == TypeDatabase::getType<glm::ivec2>()) {
232 auto fieldValue = field->getPtr<glm::ivec2>(component);
233 if (ImGui::DragInt2(fieldHeader.c_str(), glm::value_ptr(*fieldValue))) {
234 component->setFieldChanged(fieldId);
235 }
236 }
237 else if (fieldType == TypeDatabase::getType<glm::ivec3>()) {
238 auto fieldValue = field->getPtr<glm::ivec3>(component);
239 if (ImGui::DragInt3(fieldHeader.c_str(), glm::value_ptr(*fieldValue))) {
240 component->setFieldChanged(fieldId);
241 }
242 }
243 else if (fieldType == TypeDatabase::getType<glm::ivec4>()) {
244 auto fieldValue = field->getPtr<glm::ivec4>(component);
245 if (ImGui::DragInt4(fieldHeader.c_str(), glm::value_ptr(*fieldValue))) {
246 component->setFieldChanged(fieldId);
247 }
248 }
249 else if (fieldType == TypeDatabase::getType<glm::uvec2>()) {
250 auto fieldValue = field->getPtr<glm::uvec2>(component);
251 glm::ivec2 tmp = *fieldValue;
252 if (ImGui::DragInt2(fieldHeader.c_str(), glm::value_ptr(tmp))) {
253 *fieldValue = tmp;
254 component->setFieldChanged(fieldId);
255 }
256 }
257 else if (fieldType == TypeDatabase::getType<glm::uvec3>()) {
258 auto fieldValue = field->getPtr<glm::uvec3>(component);
259 glm::ivec3 tmp = *fieldValue;
260 if (ImGui::DragInt3(fieldHeader.c_str(), glm::value_ptr(tmp))) {
261 *fieldValue = tmp;
262 component->setFieldChanged(fieldId);
263 }
264 }
265 else if (fieldType == TypeDatabase::getType<glm::uvec4>()) {
266 auto fieldValue = field->getPtr<glm::uvec4>(component);
267 glm::ivec4 tmp = *fieldValue;
268 if (ImGui::DragInt4(fieldHeader.c_str(), glm::value_ptr(tmp))) {
269 *fieldValue = tmp;
270 component->setFieldChanged(fieldId);
271 }
272 }
273 else if (fieldType == TypeDatabase::getType<glm::dvec2>()) {
274 auto fieldValue = field->getPtr<glm::dvec2>(component);
275
276 glm::vec2 fValues(*fieldValue);
277
278 if (ImGui::DragFloat2(fieldHeader.c_str(), glm::value_ptr(fValues))) {
279 *fieldValue = glm::dvec2(fValues);
280 component->setFieldChanged(fieldId);
281 }
282 }
283 else if (fieldType == TypeDatabase::getType<glm::dvec3>()) {
284 auto fieldValue = field->getPtr<glm::dvec3>(component);
285
286 glm::vec3 fValues(*fieldValue);
287
288 if (ImGui::DragFloat3(fieldHeader.c_str(), glm::value_ptr(fValues))) {
289 *fieldValue = glm::dvec3(fValues);
290 component->setFieldChanged(fieldId);
291 }
292 }
293 else if (fieldType == TypeDatabase::getType<glm::quat>()) {
294 auto fieldValue = field->getPtr<glm::quat>(component);
295 // Euler Angles
296 glm::vec3 euler = glm::eulerAngles(*fieldValue);
297 if (ImGui::DragFloat3("Euler", glm::value_ptr(euler), 0.01f, -3.14f, 3.14f)) {
298 float eps = 0.00001f;
299 if (euler.y >= glm::pi<float>() / 2.0f - eps)
300 euler.y = glm::pi<float>() / 2.0f - eps;
301 else if (euler.y <= -glm::pi<float>() / 2.0f + eps)
302 euler.y = -glm::pi<float>() / 2.0f + eps;
303 *fieldValue = glm::quat(euler);
304
305 component->setFieldChanged(fieldId);
306 }
307
308 if (ImGui::DragFloat4(fieldHeader.c_str(), glm::value_ptr(*fieldValue), 0.01f, 0, 1)) {
309 component->setFieldChanged(fieldId);
310 }
311 }
312 else if (fieldType == TypeDatabase::getType<glm::mat4>()) {
313 auto fieldValue = field->getPtr<glm::mat4>(component);
314
315 ImGui::PushID(field);
316
317 if (showMatrix(fieldHeader, *fieldValue)) {
318 component->setFieldChanged(fieldId);
319 }
320 ImGui::PopID();
321 }
322 else if (fieldType == TypeDatabase::getType<bool>()) {
323 auto fieldValue = field->getPtr<bool>(component);
324
325 if (ImGui::Checkbox(fieldHeader.c_str(), fieldValue)) {
326 component->setFieldChanged(fieldId);
327 }
328 }
329 else if (fieldType == TypeDatabase::getType<uint32_t>()) {
330 auto fieldValue = field->getPtr<uint32_t>(component);
331 ImGui::Text("%s: 0x%08x", fieldHeader.c_str(), *fieldValue);
332
333 int tmp = static_cast<int>(*fieldValue);
334 const auto* range = field->get<ComponentModel::RangeAttribute<uint32_t>>();
335 if (range) {
336 const int min = std::min(tmp, static_cast<int>(range->getMin()));
337 const int max = std::max(tmp, static_cast<int>(range->getMax()));
338 if (ImGui::SliderInt(fieldHeader.c_str(), &tmp, min, max)) {
339 *fieldValue = static_cast<uint32_t>(tmp);
340 component->setFieldChanged(fieldId);
341 }
342 }
343 else {
344 if (ImGui::DragInt(fieldHeader.c_str(), &tmp)) {
345 *fieldValue = static_cast<uint32_t>(tmp);
346 component->setFieldChanged(fieldId);
347 }
348 }
349 }
350 else if (fieldType.isEnum()) {
351 auto fieldValue = field->getPtr<uint32_t>(component);
352 auto value = *fieldValue;
353
354 if (fieldType.isEnumFlags()) {
355 ImGui::Text("%s (%d = 0x%08X):", fieldHeader.c_str(), value, value);
356 ScopedIndent si;
357
358 ImGui::PushID(field);
359
360 auto numEnums = fieldType.getNumEnumerators();
361
362 for (size_t i = 0; i < numEnums; ++i) {
363 auto enumerator = fieldType.getEnumerator(i);
364 auto enumeratorValue = enumerator->getValue();
365
366 if (std::popcount(static_cast<uint32_t>(enumeratorValue)) != 1) continue;
367
368 if (ImGui::CheckboxFlags(enumerator->getName().c_str(), &value, enumeratorValue)) {
369 *fieldValue = value;
370 }
371 }
372
373 ImGui::PopID();
374 }
375 else {
376 size_t enums = fieldType.getNumEnumerators();
377
378 std::vector<const char *> enumNames(enums);
379 std::vector<int> enumValues(enums);
380 int index = 0;
381 for (size_t i = 0; i < enums; ++i) {
382 enumNames[i] = fieldType.getEnumerator(i)->getName().c_str();
383 enumValues[i] = fieldType.getEnumerator(i)->getValue();
384 if (enumValues[i] == static_cast<int>(*fieldValue)) index = static_cast<int>(i);
385 }
386
387 if (ImGui::Combo(fieldHeader.c_str(), &index, enumNames.data(), static_cast<int>(enumNames.size()))) {
388 *fieldValue = enumValues[index];
389 component->setFieldChanged(fieldId);
390 }
391 }
392 }
393 else if (fieldType == TypeDatabase::getType<std::vector<EntityPtr>>()) {
394 auto fieldValue = field->getPtr<std::vector<EntityPtr>>(component);
395 showEntities(context, *fieldValue, entityNamePattern, componentNamePattern);
396 }
397 else if (fieldType == TypeDatabase::getType<MeshHandle>()) {
398 auto & fieldValue = *field->getPtr<MeshHandle>(component);
399
400 if (fieldValue) {
401 showMesh(context, fieldValue.resolve(), fieldHeader);
402 } else {
403 ImGui::Text("None");
404 }
405 }
406 else if (fieldType == TypeDatabase::getType<TextureHandle>()) {
407 auto fieldValue = field->getPtr<TextureHandle>(component);
408
409 auto editor = context->engine->getEditor();
410 if (editor && editor->isActive()) {
411 if (editor->showTexture(field->getName().c_str(), *fieldValue)) {
412 component->setFieldChanged(fieldId);
413 }
414 }
415 else {
416 if (*fieldValue) {
417 if (ImGui::CollapsingHeader(field->getName().c_str())) {
418 Texture* texture = fieldValue->resolve();
419 auto renderer = dynamic_cast<Cogs::Core::Renderer*>(context->renderer);
420 showTexture(context, renderer, texture);
421 }
422 }
423 else {
424 ImGui::Text("%s: NULL", field->getName().c_str());
425 }
426 }
427 }
428 else if (fieldType == TypeDatabase::getType<WeakEntityPtr>()) {
429 auto fieldValue = field->getPtr<WeakEntityPtr>(component);
430 if (auto spt = fieldValue->lock())
431 {
432 showEntityInspector(context, &(*spt), entityNamePattern, componentNamePattern, showBorder);
433 } else {
434 ImGui::Text("%s: NULL", field->getName().c_str());
435 }
436 }
437 else if (fieldType == TypeDatabase::getType<std::string>()) {
438 auto fieldValue = field->getPtr<std::string>(component);
439 std::string buffer = *fieldValue;
440 buffer.resize(256);
441 if (ImGui::InputText(field->getName().c_str(), &buffer[0], buffer.size())) {
442 for (size_t i = 0; i < buffer.size(); i++) {
443 if (buffer[i] == '\0') {
444 buffer.resize(i);
445 break;
446 }
447 }
448
449 ImguiRenderer* guiRenderer = context->renderer->getGuiRenderer();
450 if (guiRenderer) {
451 guiRenderer->addTextToGlyphBuilder(buffer.c_str());
452 }
453
454 *fieldValue = std::move(buffer);
455 }
456 }
457 else if (fieldType == TypeDatabase::getType<std::vector<std::string>>()) {
458 auto fieldValue = field->getPtr<std::vector<std::string>>(component);
459 if (fieldValue->empty())
460 {
461 ImGui::Text("%s: Empty", field->getName().c_str());
462 }
463 else {
464 ScopedIndent si;
465 if (ImGui::CollapsingHeader(std::string(fieldHeader + " [size=" + std::to_string(fieldValue->size()) + "]").c_str()))
466 {
467 int i = 0;
468 for (auto& s : *fieldValue) {
469 auto label = std::to_string(i++);
470 std::string buffer = s;
471 buffer.resize(256);
472 if (ImGui::InputText(label.c_str(), &buffer[0], buffer.size())) {
473
474 ImguiRenderer* guiRenderer = context->renderer->getGuiRenderer();
475 if (guiRenderer) {
476 guiRenderer->addTextToGlyphBuilder(buffer.c_str());
477 }
478
479 s = buffer;
480 }
481 }
482 }
483 }
484 }
485 else if (fieldType == TypeDatabase::getType<std::vector<int32_t>>()) {
486 auto fieldValue = field->getPtr<std::vector<int32_t>>(component);
487 if (showArray1D(field->getName().getName(), std::span<int32_t>(*fieldValue))) {
488 component->setFieldChanged(fieldId);
489 }
490 }
491 else if (fieldType == TypeDatabase::getType<std::vector<uint32_t>>()) {
492 auto fieldValue = field->getPtr<std::vector<uint32_t>>(component);
493 if (showArray1D(field->getName().getName(), std::span<uint32_t>(*fieldValue))) {
494 component->setFieldChanged(fieldId);
495 }
496 }
497 else if (fieldType == TypeDatabase::getType<std::vector<float>>()) {
498 auto fieldValue = field->getPtr<std::vector<float>>(component);
499 if (showArray1D(field->getName().getName(), std::span<float>(*fieldValue))) {
500 component->setFieldChanged(fieldId);
501 }
502 }
503 else if (fieldType == TypeDatabase::getType<std::vector<glm::vec3>>()) {
504 auto fieldValue = field->getPtr<std::vector<glm::vec3>>(component);
505 if (showArray3D(fieldHeader, *fieldValue)) {
506 component->setFieldChanged(fieldId);
507 }
508 }
509 else if (fieldType == TypeDatabase::getType<std::vector<glm::vec2>>()) {
510 auto fieldValue = field->getPtr<std::vector<glm::vec2>>(component);
511 if (fieldValue->empty())
512 {
513 ImGui::Text("%s: Empty", field->getName().c_str());
514 }
515 else if (ImGui::CollapsingHeader(fieldHeader.c_str())) {
516 ScopedIndent si;
517 int i = 0;
518 for (auto& v : *fieldValue) {
519 auto label = std::to_string(i++);
520 if (ImGui::DragFloat2(label.c_str(), glm::value_ptr(v))) {
521 component->setFieldChanged(fieldId);
522 }
523 }
524 }
525 }
526 else if (fieldType == TypeDatabase::getType<std::vector<glm::vec4>>()) {
527 auto fieldValue = field->getPtr<std::vector<glm::vec4>>(component);
528 if (fieldValue->empty()) {
529 ImGui::Text("%s: Empty", field->getName().c_str());
530 }
531 else if (ImGui::CollapsingHeader(fieldHeader.c_str())) {
532 ScopedIndent si;
533 int i = 0;
534 for (auto& v : *fieldValue) {
535 auto label = std::to_string(i++);
536 if (ImGui::DragFloat4(label.c_str(), glm::value_ptr(v))) {
537 component->setFieldChanged(fieldId);
538 }
539 }
540 }
541 }
542 else if (fieldType == TypeDatabase::getType<MaterialInstanceHandle>()) {
543 auto fieldValue = field->getPtr<MaterialInstanceHandle>(component);
544 auto header = (!field->getName().getName().empty() ? field->getName().getName() : "Material");
545 auto * material = fieldValue->resolve();
546 if (material) {
547 showMaterialInstance(context, material, header);
548 }
549 else {
550 ImGui::Text("%s: NULL", header.c_str());
551 }
552 }
553 else if (fieldType == TypeDatabase::getType<std::vector<MaterialInstanceHandle>>()) {
554 auto fieldValue = field->getPtr<std::vector<MaterialInstanceHandle>>(component);
555 if (fieldValue->empty()) {
556 ImGui::Text("%s: Empty", field->getName().c_str());
557 }
558 else if (ImGui::CollapsingHeader(fieldHeader.c_str())) {
559 ScopedIndent si;
560 int i = 0;
561 for (auto& v : *fieldValue) {
562 auto* material = v.resolve();
563
564 auto label = "Mat: " + std::to_string(i++);
565 if (material) {
566 showMaterialInstance(context, material, label);
567 }
568 else {
569 ImGui::Text("%s: NULL", label.c_str());
570 }
571 }
572 }
573 }
574 else if (fieldType == TypeDatabase::getType<EntityPtr>()) {
575 auto fieldValue = field->getPtr<EntityPtr>(component);
576 if (fieldValue->get()) {
577 showEntityInspector(context, fieldValue->get(), entityNamePattern, componentNamePattern, showBorder);
578 }
579 else {
580 ImGui::Text("%s: NULL", field->getName().c_str());
581 }
582 }
583 else if (fieldType == TypeDatabase::getType<Cogs::Core::FontHandle>()) {
584 auto fieldValue = field->getPtr<Cogs::Core::FontHandle>(component);
585 if (fieldValue->get()) {
586 ImGui::Text("%s:", field->getName().c_str());
587 ImGui::SameLine();
588 ImGui::Text("%d", fieldValue->getId());
589 }
590 else {
591 ImGui::Text("%s: NULL", field->getName().c_str());
592 }
593 }
594 else if (fieldType == TypeDatabase::getType<ModelHandle>()) {
595 auto fieldValue = field->getPtr<ModelHandle>(component);
596
597 Model * model = fieldValue->resolve();
598 if (model) {
599 auto header = (!model->getName().empty() ? model->getName() : "Model");
600 showModel(context, model, std::string(header));
601 } else {
602 ImGui::Text("%s: NULL", field->getName().c_str());
603 }
604 }
605 else if (fieldType == TypeDatabase::getType<PropertyStore>()) {
606 PropertyStore* propStore = field->getPtr<PropertyStore>(component);
607 assert(propStore != nullptr);
608
609 for (const Cogs::Core::PropertyInfo& header : propStore->getHeaders()) {
610 const std::string propName(propStore->getKey(header));
611
612 switch(header.type) {
613 case Cogs::Core::PropertyType::Bool: {
614 ImGui::Text("%s: %s", propName.c_str(), header.boolValue ? "true" : "false");
615 break;
616 }
617 case Cogs::Core::PropertyType::Integer: {
618 ImGui::Text("%s: %d", propName.c_str(), header.intValue);
619 break;
620 }
621 case Cogs::Core::PropertyType::Int2: {
622 ImGui::Text("%s: (%d, %d)", propName.c_str(), header.int2Value[0], header.int2Value[1]);
623 break;
624 }
625 case Cogs::Core::PropertyType::UnsignedInteger: {
626 ImGui::Text("%s: %u", propName.c_str(), header.uintValue);
627 break;
628 }
629 case Cogs::Core::PropertyType::UInt2: {
630 ImGui::Text("%s: (%u, %u)", propName.c_str(), header.uint2Value[0], header.uint2Value[1]);
631 break;
632 }
633 case Cogs::Core::PropertyType::Float: {
634 ImGui::Text("%s: %f", propName.c_str(), header.floatValue);
635 break;
636 }
637 case Cogs::Core::PropertyType::Float2: {
638 ImGui::Text("%s: (%f, %f)", propName.c_str(), header.float2Value[0], header.float2Value[1]);
639 break;
640 }
641 case Cogs::Core::PropertyType::Double: {
642 ImGui::Text("%s: %f", propName.c_str(), header.doubleValue);
643 break;
644 }
645 case Cogs::Core::PropertyType::StringRef: {
646 ImGui::Text("%s: %s", propName.c_str(), Cogs::Core::Strings::getC(header.stringRefValue));
647 break;
648 }
649 case Cogs::Core::PropertyType::String: {
650 ImGui::Text("%s: %.*s", propName.c_str(), StringViewFormat(propStore->getString(header)));
651 break;
652 }
653 case Cogs::Core::PropertyType::FloatArray: {
654 showArray1D(propName, propStore->getFloatArray(header));
655 break;
656 }
657 case Cogs::Core::PropertyType::IntArray: {
658 showArray1D(propName, propStore->getIntArray(header));
659 break;
660 }
661 case Cogs::Core::PropertyType::UIntArray: {
662 showArray1D(propName, propStore->getUIntArray(header));
663 break;
664 }
665 case Cogs::Core::PropertyType::DoubleArray: {
666 showArray1D(propName, propStore->getDoubleArray(header));
667 break;
668 }
669 default: {
670 assert(false && "Unsupported PropertyType");
671 }
672 }
673 }
674 }
675 else {
676 ImGui::Text("%s: %s (Type: %d)", field->getName().c_str(), fieldType.getName().c_str(), static_cast<int>(fieldType.getTypeId()));
677 }
678}
679
680void Cogs::Core::entityInspector(Context * context, bool * show)
681{
682 if (*show) {
683 ImGui::SetNextWindowSize(ImVec2(400, 1000), ImGuiCond_Once);
684
685 guiBegin("Entities", show);
686
687 const std::unordered_map<EntityId, EntityPtr>& entities = context->store->getEntities();
688
689 // Filter out entities in store, but child of other.
690 static bool onlyRootEntities = true;
691 size_t numEntities = 0;
692 if (onlyRootEntities) {
693 for (const auto& [id, entity] : entities) {
694 if (context->store->getEntityParent(entity.get()) == nullptr) {
695 numEntities++;
696 }
697 }
698 }
699 else {
700 numEntities = entities.size();
701 }
702
703 ImGui::Text("Entities: %zu", numEntities);
704
705 const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
706 ImGui::SameLine(0.0f, spacing);
707 ImGui::Text(" ");
708 ImGui::SameLine(0.0f, spacing);
709 if (ImGui::RadioButton("Root Entities", onlyRootEntities == true)) { onlyRootEntities = true; }
710 ImGui::SameLine(0.0f, spacing);
711 if (ImGui::RadioButton("All User", onlyRootEntities == false)) { onlyRootEntities = false; }
712
713 // Add text-box allowing filtering by entity name. Simple String containsInvariantCase.
714 std::string_view entityNamePattern;
715 {
716 static std::array<char, 33> nameFilterBuffer;
717 constexpr int widthInputText = 120;
718 ImGui::PushItemWidth(widthInputText);
719 ImGui::Text("Name:");
720 ImGui::SameLine(0.0f, spacing);
721 if (ImGui::InputText(" ", nameFilterBuffer.data(), nameFilterBuffer.size(), ImGuiInputTextFlags_None)) {
722 ImguiRenderer* guiRenderer = context->renderer->getGuiRenderer();
723 if (guiRenderer) {
724 guiRenderer->addTextToGlyphBuilder(nameFilterBuffer.data());
725 }
726 }
727 entityNamePattern = nameFilterBuffer.data();
728 }
729
730 std::string_view componentNamePattern;
731 {
732 static std::array<char, 33> componentFilterBuffer;
733 ImGui::SameLine(0.0f, spacing);
734 ImGui::Text("Componen:");
735 ImGui::SameLine(0.0f, spacing);
736 if (ImGui::InputText(" ", componentFilterBuffer.data(), componentFilterBuffer.size(), ImGuiInputTextFlags_None)) {
737 ImguiRenderer* guiRenderer = context->renderer->getGuiRenderer();
738 if (guiRenderer) {
739 guiRenderer->addTextToGlyphBuilder(componentFilterBuffer.data());
740 }
741 }
742 componentNamePattern = componentFilterBuffer.data();
743 }
744
745 ImGui::PopItemWidth();
746
747 // Get entity search. Special: startes with '+' only show entities with several refs.
748 // Possible leaked refs. E.g. both a child and stored in store->entities.
749 bool showOnlyMultiRefs = false;
750 if (!entityNamePattern.empty() && entityNamePattern[0] == '+') {
751 showOnlyMultiRefs = true;
752 entityNamePattern = entityNamePattern.substr(1);
753 }
754
755
756 ImGui::BeginChild("Child");
757 for (const auto& [id, entity] : entities) {
758 bool showEntity = entity.operator bool();
759 if (showOnlyMultiRefs) {
760 showEntity = entity.use_count() > 1;
761 }
762
763 if (showEntity && (!entityNamePattern.empty() || !componentNamePattern.empty())) {
764 showEntity = findHierarchyWithMatch(*entity, entityNamePattern, componentNamePattern);
765 }
766
767 if (showEntity && onlyRootEntities) {
768 showEntity = context->store->getEntityParent(entity.get()) == nullptr;
769 }
770
771 if (showEntity) {
772 showCollapsableEntityInspector(context, entity.get(), entityNamePattern, componentNamePattern);
773 }
774 }
775 ImGui::EndChild();
776
777 guiEnd();
778 }
779}
Base class for Component instances.
Definition: Component.h:143
void setFieldChanged(const Reflection::FieldId fieldId)
Sets the component to the ComponentFlags::Changed state without carry.
Definition: Component.h:218
COGSFOUNDATION_API const Reflection::Type & getType() const
Get the full Reflection::Type of the component.
Definition: Component.cpp:27
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
constexpr size_t getId() const noexcept
Get the unique identifier of this entity.
Definition: Entity.h:113
void getComponents(ComponentCollection< T > &collection) const
Get all the components implementing the templated type.
Definition: Entity.h:52
const std::string & getName() const noexcept
Get the name of this entity.
Definition: Entity.h:120
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Core renderer system.
Definition: Renderer.h:28
Field definition describing a single data member of a data structure.
Definition: Field.h:68
const Name & getName() const
Get the name of the field.
Definition: Field.h:136
const T * get() const
Retrieve an attribute of the given type from storage, if present.
Definition: Field.h:194
TypeId getTypeId() const
Get the type id of the field.
Definition: Field.h:140
FieldValueType * getPtr(void *container) const
Get a pointer to this field on the given container.
Definition: Field.h:112
void call(Class *object, Arg... arg) const
Call the method named name on the given object, with the given arguments.
Definition: Method.h:144
static const Type & getType()
Get the Type of the given template argument.
Definition: TypeDatabase.h:168
Represents a discrete type definition, describing a native type class.
Definition: Type.h:89
const Enumerator * getEnumerator(const Name &name) const
Get a pointer to the enumerator with the given name.
Definition: Type.cpp:145
const Method * getMethod(const Name &name) const
Get a pointer to the method with the given name.
Definition: Type.cpp:133
bool isEnum() const
Get if the type is an enumeration type.
Definition: Type.h:315
constexpr const Name & getName() const
Get the unique name of the type.
Definition: Type.h:198
bool isEnumFlags() const
Get if the type is a flag enumeration type.
Definition: Type.h:334
size_t getNumHierarchyFields() const
Get the number of fields in the type + types in all base types.
Definition: Type.cpp:96
size_t getNumEnumerators() const
Get the number of enumerators in the type.
Definition: Type.h:322
const Field * getField(const Name &name) const
Get a pointer to the field info of the field with the given name.
Definition: Type.cpp:53
constexpr TypeId getTypeId() const
Get the unique Reflection::TypeId of this instance.
Definition: Type.h:325
Contains code for composing and managing entities built from components.
bool findHierarchyWithMatch(const Cogs::ComponentModel::Entity &entity, std::string_view entityNamePattern, std::string_view componentNamePattern)
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
Definition: EntityPtr.h:12
std::weak_ptr< ComponentModel::Entity > WeakEntityPtr
Weak Smart pointer for Entity access.
Definition: EntityPtr.h:18
bool showArray1D(const std::string &header, std::span< T > array)
Contains reflection support.
Definition: Component.h:11
Tags an object as being able to represent a color.
Definition: Attributes.h:122
Handle to a Component instance.
Definition: Component.h:67
Adds range information to an object.
Definition: Attributes.h:38
const Name & getName() const
Get the name of the enumerator.
Definition: Type.h:68
int getValue() const
Get the value of the enumerator.
Definition: Type.h:71
Represents an unique name.
Definition: Name.h:70
const char * c_str() const
Gets the name as a null-terminated string.
Definition: Name.h:116
const std::string & getName() const
Get the string name. This can be empty, even in valid instances of Name.
Definition: Name.h:112