1#include "InspectorGuiHelper.h"
5#include "Components/Core/SceneComponent.h"
7#include "Renderer/Renderer.h"
9#include "Resources/TextureManager.h"
10#include "Renderer/RenderTexture.h"
12#include "Resources/Mesh.h"
13#include "Resources/MeshManager.h"
14#include "Resources/MaterialOptions.h"
15#include "Resources/MaterialInstance.h"
16#include "Resources/Model.h"
17#include "Resources/Animation.h"
21#include <glm/vec2.hpp>
22#include <glm/vec3.hpp>
23#include <glm/vec4.hpp>
24#include <glm/mat4x4.hpp>
25#include <glm/gtc/type_ptr.hpp>
33 bool lc_eq(
unsigned char l,
unsigned char r)
35 return (std::tolower(l) == std::tolower(r));
39std::string Cogs::Core::guiWindowTitle;
40std::vector<size_t> Cogs::Core::guiIdsStack;
42enum struct EMeshDataType : uint8_t
54 EMeshDataType type = EMeshDataType::Other;
58 ImU32 color = IM_COL32(255, 255, 255, 255);
64constexpr float normalizeNumber(T value) {
65 return std::max(
static_cast<float>(value) /
static_cast<float>(std::numeric_limits<T>::max()), -1.0f);
68void Cogs::Core::guiBegin(
const std::string & title,
bool * show)
70 ImGui::Begin(title.c_str(), show, 0);
71 guiWindowTitle = title;
73 assert(guiIdsStack.empty());
76void Cogs::Core::guiEnd()
82std::string Cogs::Core::getUniqueHeader(
const std::string & header)
84 if (guiIdsStack.empty()) {
85 guiIdsStack.push_back(0);
90 assert(!guiWindowTitle.empty());
91 std::string
id = guiWindowTitle;
92 for (
auto top : guiIdsStack) {
96 id += std::to_string(top);
100 res.reserve(header.size() + 2 +
id.size());
109void Cogs::Core::appendBytesize(std::string& out,
size_t byteSize)
111 std::array<char, 32> tmp;
113 constexpr size_t k =
static_cast<size_t>(1024);
114 constexpr size_t m = k * k;
115 constexpr size_t g = k * k * k;
118 out.append(std::to_string(byteSize));
121 else if (byteSize < m) {
122 if (
int n = snprintf(tmp.data(), tmp.size(),
"%.1f",
double(byteSize) /
double(k)); 0 <= n && size_t(n) < tmp.size()) {
123 out.append(std::string_view(tmp.data(), n));
127 else if (byteSize < g) {
128 if (
int n = snprintf(tmp.data(), tmp.size(),
"%.1f",
double(byteSize) /
double(m)); 0 <= n && size_t(n) < tmp.size()) {
129 out.append(std::string_view(tmp.data(), n));
134 if (
int n = snprintf(tmp.data(), tmp.size(),
"%.1f",
double(byteSize) /
double(g)); 0 <= n && size_t(n) < tmp.size()) {
135 out.append(std::string_view(tmp.data(), n));
142bool Cogs::Core::containsInvariantCase(std::string_view str, std::string_view substr)
144 return std::search(str.begin(), str.end(), substr.begin(), substr.end(), lc_eq) != str.end();
150 if (entityNamePattern.empty() && componentNamePattern.empty()) {
154 bool showEntity =
true;
155 if (!entityNamePattern.empty() && !containsInvariantCase(entity.
getName(), entityNamePattern)) {
159 if (showEntity && !componentNamePattern.empty()) {
164 showEntity = showEntity || containsInvariantCase(component->
getType().
getName().
c_str(), componentNamePattern);
172 if (sceneComponent) {
184void Cogs::Core::showTexture(
const Context * context, Renderer * renderer, Texture * texture,
bool showTitle)
187 if (!texture->getName().empty()) {
188 ImGui::Text(
"Name: %.*s", StringViewFormat(texture->getName()));
190 else if (texture->getId() != NoResourceId) {
191 ImGui::Text(
"Id: %d", texture->getId());
195 ImGui::Text(
"Refs: %u", texture->referenceCount());
197 ImGui::Text(
"Target: %s", getResourceDimensionsName(texture->description.target));
199 switch(texture->description.target){
200 case ResourceDimensions::Unknown:
201 ImGui::Text(
"Size: Unknown");
203 case ResourceDimensions::Buffer:
204 ImGui::Text(
"Size: Buffer");
206 case ResourceDimensions::Texture1D:
207 ImGui::Text(
"Size: %d", texture->description.width);
209 case ResourceDimensions::Texture1DArray:
210 ImGui::Text(
"Size: %d Layers %d", texture->description.width, texture->description.layers);
212 case ResourceDimensions::Texture2D:
213 ImGui::Text(
"Size: %dx%d", texture->description.width, texture->description.height);
215 case ResourceDimensions::Texture2DArray:
216 ImGui::Text(
"Size: %dx%d Layers %d", texture->description.width, texture->description.height, texture->description.layers);
218 case ResourceDimensions::Texture2DMS:
219 ImGui::Text(
"Size: %dx%d Samples %d", texture->description.width, texture->description.height, texture->description.samples);
221 case ResourceDimensions::Texture2DMSArray:
222 ImGui::Text(
"Size: %dx%d Layers %d Samples %d", texture->description.width, texture->description.height, texture->description.layers, texture->description.samples);
224 case ResourceDimensions::Texture3D:
225 ImGui::Text(
"Size: %dx%dx%d", texture->description.width, texture->description.height, texture->description.depth);
227 case ResourceDimensions::Texture3DArray:
228 ImGui::Text(
"Size: %dx%dx%d Layers %d", texture->description.width, texture->description.height, texture->description.depth, texture->description.layers);
230 case ResourceDimensions::TextureCube:
231 ImGui::Text(
"Size: %dx%d Faces %d", texture->description.width, texture->description.height, texture->description.faces);
233 case ResourceDimensions::TextureCubeArray:
234 ImGui::Text(
"Size: %dx%d Faces %d Layers %d", texture->description.width, texture->description.height, texture->description.faces, texture->description.layers);
236 case ResourceDimensions::RenderBuffer:
237 ImGui::Text(
"Size: %dx%d", texture->description.width, texture->description.height);
239 case ResourceDimensions::ResourceDimensions_Size:
243 ImGui::Text(
"Mips %d", texture->description.levels);
245 auto tmp = getFormatInfo(texture->description.format);
247 ImGui::Text(
"Format: %s", tmp->name);
249 RenderTexture * renderTexture = renderer->getRenderResources().getRenderTexture(context->textureManager->generateHandle(texture));
251 if (renderTexture &&
HandleIsValid(renderTexture->textureHandle)) {
252 float aspectRatio = 1;
253 if (texture->description.width > 0 && texture->description.height > 0) {
254 aspectRatio =
static_cast<float>(texture->description.width) /
static_cast<float>(texture->description.height);
257 auto size = ImVec2(256, 256);
258 if (aspectRatio < 1) {
259 size.x *= aspectRatio;
261 size.y /= aspectRatio;
264 uint64_t mode = GUI_MODE_TEX_TYPE_2D;
265 if (texture->description.target == ResourceDimensions::Texture2DMS) {
266 mode = GUI_MODE_TEX_TYPE_2DMS;
268 else if (texture->description.target == ResourceDimensions::Texture1DArray ||
269 texture->description.target == ResourceDimensions::Texture2DArray) {
270 mode = GUI_MODE_TEX_TYPE_ARRAY;
272 else if (texture->description.target == ResourceDimensions::TextureCube ||
273 texture->description.target == ResourceDimensions::TextureCubeArray) {
274 mode = GUI_MODE_TEX_TYPE_CUBE;
277 auto dl = ImGui::GetWindowDrawList();
278 dl->AddCallback(setGuiMode,
reinterpret_cast<void*
>(mode));
279 ImGui::Image(ImTextureID(renderTexture->textureHandle.handle), size);
281 dl->AddCallback(setGuiMode,
reinterpret_cast<void*
>(mode | GUI_MODE_TEX_CHANNELS_ALPHA));
282 ImGui::Image(ImTextureID(renderTexture->textureHandle.handle), size);
283 dl->AddCallback(setGuiMode,
reinterpret_cast<void*
>(GUI_MODE_DEFAULT));
287void Cogs::Core::showRenderTexture(
const Context * , Renderer * ,
const RenderTexture * renderTexture,
bool showTitle)
289 auto texture = renderTexture->getResource();
290 float aspectRatio = 1;
294 if (!texture->getName().empty()) {
295 ImGui::Text(
"Name: %.*s", StringViewFormat(texture->getName()));
297 else if (texture->getId() != NoResourceId) {
298 ImGui::Text(
"Id: %d", texture->getId());
302 ImGui::Text(
"Refs: %u", texture->referenceCount());
303 ImGui::Text(
"Size: %d x %d", renderTexture->description.width, renderTexture->description.height);
305 if (renderTexture->description.width > 0 && renderTexture->description.height > 0) {
306 aspectRatio =
static_cast<float>(renderTexture->description.width) /
static_cast<float>(renderTexture->description.height);
310 if (renderTexture &&
HandleIsValid(renderTexture->textureHandle)) {
311 ImVec2 size(256, 256);
312 ImDrawList* dl = ImGui::GetWindowDrawList();
314 if (aspectRatio < 1) {
315 size.x *= aspectRatio;
318 size.y /= aspectRatio;
321 if (renderTexture->description.target == ResourceDimensions::Texture2DMS) {
322 dl->AddCallback(setGuiMode,
reinterpret_cast<void*
>(GUI_MODE_TEX_TYPE_2DMS));
323 ImGui::Image(ImTextureID(renderTexture->textureHandle.handle), size);
325 dl->AddCallback(setGuiMode,
reinterpret_cast<void*
>(GUI_MODE_TEX_TYPE_2DMS | GUI_MODE_TEX_CHANNELS_ALPHA));
326 ImGui::Image(ImTextureID(renderTexture->textureHandle.handle), size);
327 dl->AddCallback(setGuiMode,
reinterpret_cast<void*
>(GUI_MODE_DEFAULT));
330 ImGui::Image(ImTextureID(renderTexture->textureHandle.handle), size);
332 dl->AddCallback(setGuiMode,
reinterpret_cast<void*
>(GUI_MODE_TEX_CHANNELS_ALPHA));
333 ImGui::Image(ImTextureID(renderTexture->textureHandle.handle), size);
334 dl->AddCallback(setGuiMode,
reinterpret_cast<void*
>(GUI_MODE_DEFAULT));
339bool Cogs::Core::showMatrix(
const std::string & header, glm::mat4& m)
341 bool changed =
false;
342 if (ImGui::TreeNode(header.c_str())) {
345 if (ImGui::DragFloat4(
"row_0", glm::value_ptr(m))) {
348 if (ImGui::DragFloat4(
"row_1", glm::value_ptr(m) + 4)) {
351 if (ImGui::DragFloat4(
"row_2", glm::value_ptr(m) + 8)) {
354 if (ImGui::DragFloat4(
"row_3", glm::value_ptr(m) + 12)) {
364bool Cogs::Core::showArray3D(
const std::string & header, std::span<glm::vec3> array)
366 bool changed =
false;
369 ImGui::Text(
"%s: Empty", header.c_str());
371 if (ImGui::CollapsingHeader(getUniqueHeader(std::string(header +
" [size=" + std::to_string(array.size()) +
"]")).c_str())) {
375 for (
auto& v : array) {
376 auto label = std::to_string(i++);
378 if (ImGui::DragFloat3(getUniqueHeader(label).c_str(), glm::value_ptr(v))) {
388void Cogs::Core::showMesh(
const Context * , Mesh * mesh,
const std::string & header)
392 if (ImGui::TreeNode(header.c_str())) {
394 if (ImGui::TreeNode(
"Bounding box")) {
396 bool changed = ImGui::DragFloat3(
"min", glm::value_ptr(mesh->boundingBox.min));
397 if (ImGui::DragFloat3(
"max", glm::value_ptr(mesh->boundingBox.max))) changed =
true;
399 mesh->setMeshFlag(MeshFlags::BoundingBoxSet);
404 showEnum<Cogs::PrimitiveType::EPrimitiveType>(
"Primitive type", mesh->primitiveType);
406 ImGui::Text(
"Number of primitives: %d", mesh->getCount());
408 if (mesh->isIndexed()) {
409 if (mesh->hasIndexesU16()) {
417 if (!mesh->streams.empty()) {
418 if (ImGui::TreeNode((
"Streams [size=" + std::to_string(mesh->streams.size()) +
"]").c_str())) {
421 for (DataStream& stream : mesh->streams)
423 std::string typeAsString;
426 case VertexDataType::Positions:
427 typeAsString =
"Positions";
429 case VertexDataType::Normals:
430 typeAsString =
"Normals";
432 case VertexDataType::Tangents:
433 typeAsString =
"Tangents";
435 case VertexDataType::Bitangents:
436 typeAsString =
"Bitangents";
438 case VertexDataType::TexCoords0:
439 typeAsString =
"TexCoords0";
441 case VertexDataType::TexCoords1:
442 typeAsString =
"TexCoords1";
444 case VertexDataType::TexCoords2:
445 typeAsString =
"TexCoords2";
447 case VertexDataType::Colors0:
448 typeAsString =
"Colors0";
450 case VertexDataType::Colors1:
451 typeAsString =
"Colors1";
453 case VertexDataType::Colors2:
454 typeAsString =
"Colors2";
456 case VertexDataType::Colors3:
457 typeAsString =
"Colors3";
459 case VertexDataType::Interleaved0:
460 typeAsString =
"Interleaved0";
462 case VertexDataType::Interleaved1:
463 typeAsString =
"Interleaved1";
465 case VertexDataType::Indexes:
466 typeAsString =
"Indexes";
468 case VertexDataType::PoseIndexes:
469 typeAsString =
"PoseIndexes";
471 case VertexDataType::SubMeshes:
472 typeAsString =
"SubMeshes";
474 case VertexDataType::Reserved:
475 typeAsString =
"Reserved";
481 ImGui::PushID(&stream);
483 std::string stream_header;
484 stream_header.reserve(50);
485 stream_header.append(
"Stream ");
486 stream_header.append(!typeAsString.empty() ? typeAsString : std::to_string(streamNum++));
487 stream_header.append(
" [elements=");
488 stream_header.append(std::to_string(stream.numElements));
489 stream_header.append(
", stride=");
490 stream_header.append(std::to_string(stream.stride));
491 stream_header.append(
", size=");
492 appendBytesize(stream_header, stream.size());
493 stream_header.append(
"]");
495 if (ImGui::TreeNode(stream_header.c_str())) {
498 static constexpr std::array colors = { IM_COL32(255,255,128,255), IM_COL32(255,128,255,255), IM_COL32(128,255,255,255),
499 IM_COL32(255,200,200,255), IM_COL32(200,255,200,255), IM_COL32(200,200,255,255) };
503 if (format && ImGui::TreeNode((
"Vertex Format Elements [size=" + std::to_string(format->
elements.size()) +
"]").c_str())) {
505 int colorElement = 0;
506 for (uint16_t i = 0; i < format->
elements.size(); ++i) {
510 ImGui::PushStyleColor(ImGuiCol_Text, colors[colorElement]);
511 colorElement = (colorElement + 1) % colors.size();
513 std::string elementHeader;
514 elementHeader.reserve(127);
515 elementHeader += std::to_string(i);
516 elementHeader +=
": [offset=";
517 elementHeader += std::to_string(element.
offset);
518 elementHeader +=
", size=";
519 elementHeader += std::to_string(formatInfo->
blockSize);
520 elementHeader +=
"]";
522 if (ImGui::TreeNode(elementHeader.c_str())) {
523 ImGui::Text(
"DataFormat: %s", formatInfo->
name);
525 std::string flagsStr;
526 const FormatFlags flags = formatInfo->
flags;
527 if ((flags & FormatFlags::Alpha) != FormatFlags::None) { flagsStr +=
"Alpha "; }
528 if ((flags & FormatFlags::VertexFormat) != FormatFlags::None) { flagsStr +=
"VertexFormat "; }
529 if ((flags & FormatFlags::Depth) != FormatFlags::None) { flagsStr +=
"Depth "; }
530 if ((flags & FormatFlags::Typeless) != FormatFlags::None) { flagsStr +=
"Typeless "; }
531 if ((flags & FormatFlags::Integer) != FormatFlags::None) { flagsStr +=
"Integer "; }
532 if ((flags & FormatFlags::Unsigned) != FormatFlags::None) { flagsStr +=
"Unsigned "; }
533 ImGui::Text(
"Flags: %s", flagsStr.c_str());
535 ImGui::Text(
"Semantic: %.*s%u", StringViewFormat(Cogs::getElementSemanticName(element.
semantic)), element.
semanticIndex);
536 const char* inputType = element.
inputType == InputType::VertexData ?
"VertexData" :
"InstanceData";
537 ImGui::Text(
"InputType: %s [step=%u]", inputType, element.
instanceStep);
541 ImGui::PopStyleColor();
547 if (format && ImGui::TreeNode(
"Data")) {
548 std::vector<ColumnInfo> columns;
557 column.color = colors[colorIndex];
558 colorIndex = (colorIndex + 1) % colors.size();
561 if (element.
semantic == ElementSemantic::Position) { prefix =
"V"; }
562 else if (element.
semantic == ElementSemantic::Normal) { prefix =
"N"; }
563 else if (element.
semantic == ElementSemantic::Color) { prefix =
"C"; }
564 else if (element.
semantic == ElementSemantic::TextureCoordinate) { prefix =
"T"; }
565 else if (element.
semantic == ElementSemantic::Tangent) { prefix =
"Tg"; }
566 else if (element.
semantic == ElementSemantic::InstanceVector) { prefix =
"IV"; }
567 else if (element.
semantic == ElementSemantic::InstanceMatrix) { prefix =
"IM"; }
568 else if (element.
semantic == ElementSemantic::Semantic_Size) { prefix =
"S"; }
570 std::string_view name(formatInfo->
name);
571 if (name.find(
"UINT") != std::string_view::npos) {
572 column.type = EMeshDataType::UnsignedInt;
574 else if (name.find(
"SINT") != std::string_view::npos) {
575 column.type = EMeshDataType::Integer;
577 else if (name.find(
"FLOAT") != std::string_view::npos) {
578 column.type = EMeshDataType::Float;
580 else if (name.find(
"UNORM") != std::string_view::npos) {
581 column.type = EMeshDataType::UnsignedNorm;
583 else if (name.find(
"SNORM") != std::string_view::npos) {
584 column.type = EMeshDataType::Normalized;
587 for (uint16_t j = 0; j < formatInfo->
elements; ++j) {
588 column.offset = element.
offset + j * column.size;
590 column.header = prefix +
"." + std::to_string(j);
591 columns.push_back(column);
596 const char* p =
static_cast<const char*
>(stream.data());
597 ImGui::Columns(
static_cast<int>(columns.size() + 1));
601 ImGui::Text(
"%s", column.header.c_str());
605 for (
size_t k = 0; k < stream.numElements; k++) {
606 ImGui::Text(
"%zu", k);
610 const void* data = p + column.offset;
612 ImGui::PushStyleColor(ImGuiCol_Text, column.color);
614 if (column.type == EMeshDataType::Float) {
615 if (column.size == 4) {
616 ImGui::Text(
"%.2f", *(
static_cast<const float*
>(data)));
619 else if (column.size == 2) {
620 ImGui::Text(
"%.2f", glm::detail::toFloat32(*
static_cast<const uint16_t*
>(data)));
624 else if (column.type == EMeshDataType::Integer) {
625 if (column.size == 1) {
626 ImGui::Text(
"%d", *
static_cast<const int8_t*
>(data));
629 else if (column.size == 2) {
630 ImGui::Text(
"%d", *
static_cast<const int16_t*
>(data));
633 else if (column.size == 4) {
634 ImGui::Text(
"%d", *
static_cast<const int32_t*
>(data));
638 else if (column.type == EMeshDataType::UnsignedInt) {
639 if (column.size == 1) {
640 ImGui::Text(
"%d", *
static_cast<const uint8_t*
>(data));
643 else if (column.size == 2) {
644 ImGui::Text(
"%d", *
static_cast<const uint16_t*
>(data));
647 else if (column.size == 4) {
648 ImGui::Text(
"%d", *
static_cast<const uint32_t*
>(data));
652 else if (column.type == EMeshDataType::Normalized) {
653 if (column.size == 1) {
654 ImGui::Text(
"%.2f", normalizeNumber<int8_t>(*
static_cast<const int8_t*
>(data)));
657 else if (column.size == 2) {
658 ImGui::Text(
"%.2f", normalizeNumber<int16_t>(*
static_cast<const int16_t*
>(data)));
661 else if (column.size == 4) {
662 ImGui::Text(
"%.2f", normalizeNumber<int32_t>(*
static_cast<const int32_t*
>(data)));
666 else if (column.type == EMeshDataType::UnsignedNorm) {
667 if (column.size == 1) {
668 ImGui::Text(
"%.2f", normalizeNumber<uint8_t>(*
static_cast<const uint8_t*
>(data)));
671 else if (column.size == 2) {
672 ImGui::Text(
"%.2f", normalizeNumber<uint16_t>(*
static_cast<const uint16_t*
>(data)));
675 else if (column.size == 4) {
676 ImGui::Text(
"%.2f", normalizeNumber<uint32_t>(*
static_cast<const uint32_t*
>(data)));
685 ImGui::PopStyleColor();
695 const size_t size = stream.size() /
sizeof(float);
696 const size_t intSize = stream.size() /
sizeof(int);
697 const std::span<float> data(
static_cast<float*
>(stream.data()), size);
698 const std::span<int> intData(
static_cast<int*
>(stream.data()), intSize);
699 bool streamChanged =
false;
701 if(stream.format == VertexFormats::Pos2f)
702 streamChanged = showArray2D<float>(
"Data", data);
703 else if(stream.format == VertexFormats::Pos3f)
704 streamChanged = showArray3D<float>(
"Data", data);
705 else if(stream.format == VertexFormats::Pos4f)
706 streamChanged = showArray4D<float>(
"Data", data);
707 else if(stream.format == VertexFormats::Norm3f)
708 streamChanged = showArray3D<float>(
"Data", data);
709 else if(stream.format == VertexFormats::Tang3f)
710 streamChanged = showArray3D<float>(
"Data", data);
711 else if(stream.format == VertexFormats::Tex2f)
712 streamChanged = showArray2D<float>(
"Data", data);
713 else if(stream.format == VertexFormats::Color4f)
714 streamChanged = showArray4D<float>(
"Data", data);
715 else if(stream.format == VertexFormats::BoneIndex4i)
716 streamChanged = showArray4D<int>(
"Data", intData);
717 else if(stream.format == VertexFormats::BoneWeight4f)
718 streamChanged = showArray4D<float>(
"Data", data);
724 mesh->markStreamChanged(stream.type);
736 ImGui::Text(
"Streams: Empty");
739 if (!mesh->getSubMeshes().empty()) {
740 if (ImGui::TreeNode((
"Sub-meshes [size=" + std::to_string(mesh->getSubMeshes().size()) +
"]").c_str())) {
743 for (SubMesh& subMesh : mesh->getSubMeshes()) {
744 if (ImGui::TreeNode((
"Sub-meshes " + std::to_string(
id++)).c_str())) {
746 ImGui::Text(
"Start index: %d", subMesh.startIndex);
747 ImGui::Text(
"Number of indexes: %d", subMesh.numIndexes);
748 showEnum<Cogs::PrimitiveType::EPrimitiveType>(
"Primitive type", subMesh.primitiveType);
757 ImGui::Text(
"Sub-meshes: Empty");
766void Cogs::Core::showMaterialOptions(
const Context * , MaterialOptions * options,
const std::string & header)
768 ImGui::PushID(options);
770 if (ImGui::TreeNode(header.c_str())) {
771 showEnum(
"Cull mode", options->cullMode);
773 ImGui::Checkbox(
"Depth write", &options->depthWriteEnabled);
774 ImGui::Checkbox(
"Depth test", &options->depthTestEnabled);
775 ImGui::Checkbox(
"Depth bias", &options->depthBiasEnabled);
777 if (ImGui::CollapsingHeader(
"Depth bias##DepthBiasHeader")) {
778 ImGui::DragFloat(
"Constant", &options->depthBias.constant);
779 ImGui::DragFloat(
"Slope", &options->depthBias.slope);
780 ImGui::DragFloat(
"Clamp", &options->depthBias.clamp);
783 showEnum(
"Depth func", options->depthFunc);
784 showEnum(
"Transparent", options->transparencyMode);
785 showEnum(
"Blend mode", options->blendMode);
793void Cogs::Core::showMaterialVariants(MaterialInstance* material)
795 if (ImGui::TreeNode(
"Variants")) {
796 for (
const ShaderVariantDefinition& v : material->material->definition.variants) {
798 size_t value = v.isShared ? v.defaultValue : material->variantSelectors[v.index].value;
801 case ShaderVariantType::Enum:
803 std::vector<const char*> enumNames(v.values.size());
805 for (
size_t i = 0; i < v.values.size(); ++i) {
806 enumNames[i] = v.values[i].key.c_str();
809 int currentIndex =
static_cast<int>(value);
810 if (ImGui::Combo(v.name.c_str(), ¤tIndex, enumNames.data(),
static_cast<int>(enumNames.size()))) {
811 if (!v.isShared) material->setVariant(v.name, enumNames[currentIndex]);
816 case ShaderVariantType::Bool:
817 if (
bool boolean = value != 0; ImGui::Checkbox(v.name.c_str(), &
boolean)) {
818 if (!v.isShared) material->setVariant(v.name,
boolean);
822 case ShaderVariantType::Int:
823 if (
int integer =
static_cast<int>(value); ImGui::InputInt(v.name.c_str(), &integer, 1)) {
824 if (!v.isShared) material->setVariant(v.name, integer);
829 case ShaderVariantType::Format:
833 ImGui::Text(
"Unsupported: %s", v.name.c_str());
842void Cogs::Core::showMaterialProperties(MaterialInstance* material)
844 for (
const MaterialProperty& prop : material->material->constantBuffers.variables) {
847 case MaterialDataType::Float:
850 material->getProperty(prop.key, &value,
sizeof(value));
851 if (ImGui::DragFloat(prop.name.c_str(), &value)) {
852 material->setFloatProperty(prop.key, value);
857 case MaterialDataType::Float2:
859 glm::vec2 value = {};
860 material->getProperty(prop.key, &value,
sizeof(value));
861 if (ImGui::DragFloat2(prop.name.c_str(), glm::value_ptr(value))) {
862 material->setVec2Property(prop.key, value);
867 case MaterialDataType::Float3:
869 glm::vec3 value = {};
870 material->getProperty(prop.key, &value,
sizeof(value));
871 if ((prop.flags & MaterialPropertyFlags::sRGB) == MaterialPropertyFlags::sRGB) {
872 if (ImGui::ColorEdit3(prop.name.c_str(), glm::value_ptr(value))) {
873 material->setVec3Property(prop.key, value);
877 if (ImGui::DragFloat3(prop.name.c_str(), glm::value_ptr(value))) {
878 material->setVec3Property(prop.key, value);
884 case MaterialDataType::Float4:
886 glm::vec4 value = {};
887 material->getProperty(prop.key, &value,
sizeof(value));
888 if ((prop.flags & MaterialPropertyFlags::sRGB) == MaterialPropertyFlags::sRGB) {
889 if (ImGui::ColorEdit4(prop.name.c_str(), glm::value_ptr(value))) {
890 material->setVec4Property(prop.key, value);
894 if (ImGui::DragFloat4(prop.name.c_str(), glm::value_ptr(value))) {
895 material->setVec4Property(prop.key, value);
901 case MaterialDataType::Float4x4:
903 glm::mat4 value = {};
904 material->getProperty(prop.key, &value,
sizeof(value));
905 if (showMatrix(prop.name, value)) {
906 material->setMat4Property(prop.key, value);
911 case MaterialDataType::Int:
914 material->getProperty(prop.key, &value,
sizeof(value));
915 if (ImGui::DragInt(prop.name.c_str(), &value, 1.0f, 0, 100)) {
916 material->setIntProperty(prop.key, value);
921 case MaterialDataType::UInt:
924 material->getProperty(prop.key, &value,
sizeof(value));
925 if (ImGui::DragInt(prop.name.c_str(), &value, 1.0f, 0, 100)) {
926 material->setUIntProperty(prop.key,
static_cast<uint32_t
>(value));
931 case MaterialDataType::Bool:
934 material->getProperty(prop.key, &value,
sizeof(value));
935 if (ImGui::Checkbox(prop.name.c_str(), &value)) {
936 material->setBoolProperty(prop.key, value);
942 ImGui::Text(
"Unsupported: %d",
static_cast<int>(prop.type));
948void Cogs::Core::showMaterialInstance(
const Context * context, MaterialInstance * material,
const std::string & header)
950 ImGui::PushID(material);
952 if (ImGui::TreeNodeEx(header.c_str(), ImGuiTreeNodeFlags_NoAutoOpenOnLog)) {
954 flags += material->isBackdrop() ?
"Backdrop " :
"";
955 flags += material->hasTransparency() ?
"Transparent " :
"";
956 flags += material->isDefaultMaterial() ?
"Default " :
"";
957 flags = flags.size() ?
"Flags: " + flags :
"Flags: None";
959 ImGui::Text(
"%s", flags.c_str());
961 int permutationIndex =
static_cast<int>(material->permutationIndex);
962 std::vector<const char*> permutations;
964 for (
auto& permutation : material->material->definition.permutations) {
965 permutations.push_back(permutation.permutationName.c_str());
968 if (ImGui::Combo(
"Permutation", &permutationIndex, permutations.data(),
static_cast<int>(permutations.size()))) {
969 material->setPermutation(permutations[permutationIndex]);
972 Cogs::Core::showMaterialVariants(material);
974 Cogs::Core::showMaterialOptions(context, &material->options,
"Options");
976 Cogs::Core::showMaterialProperties(material);
978 if (!material->textureVariables.empty()) {
979 if (ImGui::CollapsingHeader((
"Textures [size=" + std::to_string(material->textureVariables.size()) +
"]").c_str())) {
981 for (VariableKey texVarKey = 0; texVarKey < material->textureVariables.size(); ++texVarKey) {
982 TextureValue& texVar = material->textureVariables[texVarKey];
983 const std::string& tex_header = texVar.property->name;
986 Texture* texture = texVar.texture.handle.resolve();
988 if (ImGui::CollapsingHeader(tex_header.c_str())) {
991 showTexture(context, renderer, texture,
true);
994 TextureWithSampler textureCopy = texVar.texture;
995 ImGui::PushItemWidth(ImGui::CalcTextSize(
"Mirror").x + ImGui::GetStyle().FramePadding.x * 2.0f + ImGui::GetTextLineHeightWithSpacing());
996 if (
showEnum(
"##sAddressMode", textureCopy.sMode)) {
997 material->setTextureAddressMode(texVarKey, textureCopy.sMode, textureCopy.tMode, textureCopy.uMode);
1000 if (
showEnum(
"##tAddressMode", textureCopy.tMode)) {
1001 material->setTextureAddressMode(texVarKey, textureCopy.sMode, textureCopy.tMode, textureCopy.uMode);
1004 if (
showEnum(
"##uAddressMode", textureCopy.uMode)) {
1005 material->setTextureAddressMode(texVarKey, textureCopy.sMode, textureCopy.tMode, textureCopy.uMode);
1007 ImGui::PopItemWidth();
1010 ImGui::SetNextItemWidth(ImGui::CalcTextSize(
"ComparisonMinMagMipLinear").x + ImGui::GetStyle().FramePadding.x * 2.0f + ImGui::GetTextLineHeightWithSpacing());
1011 if (
showEnum(
"##filterMode", textureCopy.filterMode)) {
1012 material->setTextureFilterMode(texVarKey, textureCopy.filterMode);
1017 ImGui::Text(
"%s: Empty", tex_header.c_str());
1023 ImGui::Text(
"Textures: Empty");
1032void Cogs::Core::showModel(
const Context * context, Model * model,
const std::string & header)
1034 ImGui::PushID(model);
1036 if (ImGui::TreeNode(header.c_str())) {
1039 ImGui::Text(
"Loaded: %s", model->isLoaded() ?
"true" :
"false");
1041 uint32_t maxProp = model->parts.empty() ? model->properties.size() : model->parts[0].firstProperty;
1042 if (uint32_t ix = model->properties.findProperty(0, maxProp, Strings::add(
"modelOrigin")); ix != PropertyStore::NoProperty) {
1044 if (std::span<const double> view = model->properties.getDoubleArray(ix); view.size() == 3) {
1045 ImGui::Text(
"Model origin: [%.2f %.2f %.2f]", view[0], view[1], view[2]);
1050 if (ImGui::TreeNode(
"Parts",
"Parts: %zd", model->parts.size())) {
1052 for (
auto & part : model->parts) {
1053 ImGui::PushID(&part);
1055 std::string name(model->getPartName(part));
1057 std::string partName = name.empty() ? (std::string(
"Part ") + std::to_string(index)) : name;
1059 if (ImGui::TreeNode(partName.c_str())) {
1060 if (part.meshIndex != uint32_t(-1)) {
1061 ImGui::Text(
"Mesh: %u", part.meshIndex);
1063 ImGui::Text(
"Mesh: None");
1066 if (part.materialIndex != uint32_t(-1)) {
1067 ImGui::Text(
"Material: %u", part.materialIndex);
1069 ImGui::Text(
"Material: None");
1072 ImGui::Text(
"StartIndex: %ud", part.startIndex);
1073 ImGui::Text(
"VertexCount: %ud", part.vertexCount);
1076 glm::mat4 matrix = model->getPartTransform(part);
1077 if (showMatrix(
"Transform", matrix)) {
1078 model->setPartTransform(part, matrix);
1090 if (ImGui::TreeNode(
"Meshes",
"Meshes: %zd", model->meshes.size())) {
1092 for (
auto & mesh : model->meshes) {
1093 std::string meshName = mesh->getName().empty() ? (std::string(
"Mesh ") + std::to_string(index)) : std::string(mesh->getName());
1096 showMesh(context, mesh.resolve(), meshName);
1102 if (ImGui::TreeNode(
"Materials",
"Materials: %zd", model->materials.size())) {
1104 for (
auto & material : model->materials) {
1105 std::string materialName = material->getName().empty() ? (std::string(
"Material ") + std::to_string(index)) : std::string(material->getName());
1108 showMaterialInstance(context, material.resolve(), materialName);
1114 if (model->skeleton.bones.size()) {
1115 if (ImGui::TreeNode(
"Skeleton")) {
1116 if (ImGui::TreeNode(
"Bones")) {
1117 for (
auto & bone : model->skeleton.bones) {
1118 if (ImGui::TreeNode(bone.name.c_str())) {
1119 ImGui::Text(
"Name: %s", bone.name.c_str());
1120 if (bone.parentBone < model->skeleton.bones.size()) {
1121 ImGui::Text(
"Parent: %zd (%s)", bone.parentBone, model->skeleton.bones[bone.parentBone].name.c_str());
1123 ImGui::Text(
"Parent: None");
1137 if (model->animation) {
1138 if (ImGui::TreeNode(
"Animation")) {
1139 auto animation = model->animation.resolve();
1141 if (ImGui::TreeNode(
"Clips")) {
1142 for (
auto & clip : animation->clips) {
1143 if (ImGui::TreeNode(clip.name.c_str())) {
1144 ImGui::Text(
"Duration: %f", clip.duration);
1145 ImGui::Text(
"Resolution: %f", clip.resolution);
1147 if (ImGui::TreeNode(
"Tracks")) {
1148 for (
auto& track : clip.tracks) {
1149 std::string trackName =
"Bone index " + std::to_string(track.boneIndex);
1150 if (ImGui::TreeNode(trackName.c_str())) {
1151 if (track.boneIndex < animation->skeleton.bones.size()) {
1152 ImGui::Text(
"Bone: %s", animation->skeleton.bones[track.boneIndex].name.c_str());
1154 ImGui::Text(
"Keyframes: %zd", track.translations.size());
Base class for Component instances.
COGSFOUNDATION_API const Reflection::Type & getType() const
Get the full Reflection::Type of the component.
Container for components, providing composition of dynamic entities.
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
void getComponents(ComponentCollection< T > &collection) const
Get all the components implementing the templated type.
const std::string & getName() const noexcept
Get the name of this entity.
Contains information on how the entity behaves in the scene.
std::vector< EntityPtr > children
Contains all child entities owned by this component.
constexpr const Name & getName() const
Get the unique name of the type.
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.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
bool showEnum(const std::string &header, T ¤tItem)
Show enumerator with edit option.
bool showArray1D(const std::string &header, std::span< T > array)
Handle to a Component instance.
const char * c_str() const
Gets the name as a null-terminated string.
Vertex element structure used to describe a single data element in a vertex for the input assembler.
InputType inputType
Input type of the element, vertex or instance data.
DataFormat format
Format of the element.
uint16_t offset
Offset in bytes from the vertex position in memory.
uint16_t semanticIndex
Index for the semantic mapping.
ElementSemantic semantic
Semantic mapping of the element (position, normal, etc...).
uint16_t instanceStep
Instance step factor.