Skip to content

Commit 8cf3de4

Browse files
committed
Implemented changing/unassigning primitive material with compatibility check.
1 parent 832861e commit 8cf3de4

File tree

8 files changed

+243
-74
lines changed

8 files changed

+243
-74
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ target_sources(vk-gltf-viewer PRIVATE
129129
interface/gltf/SceneInverseHierarchy.cppm
130130
interface/gltf/StateCachedNodeVisibilityStructure.cppm
131131
interface/gltf/TextureUsage.cppm
132+
interface/gltf/util.cppm
132133
interface/gui/dialog.cppm
133134
interface/helpers/AggregateHasher.cppm
134135
interface/helpers/concepts.cppm

extlibs/module-ports/imgui/imgui.cppm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ namespace ImGui {
149149
export using ImGui::SetNextItemAllowOverlap;
150150
export using ImGui::SetNextItemWidth;
151151
export using ImGui::SetNextWindowPos;
152+
export using ImGui::SmallButton;
152153
export using ImGui::TableHeadersRow;
153154
export using ImGui::TableNextRow;
154155
export using ImGui::TableSetColumnIndex;

impl/MainApp.cpp

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -650,42 +650,34 @@ void vk_gltf_viewer::MainApp::run() {
650650
break;
651651
}
652652
},
653-
[&](const control::task::SelectMaterialVariants &task) {
654-
gpu.device.waitIdle();
653+
[&](const control::task::PrimitiveMaterialChanged &task) {
654+
vkgltf::PrimitiveBuffer &primitiveBuffer = dynamic_cast<vulkan::gltf::AssetExtended*>(assetExtended.get())->primitiveBuffer;
655+
const std::size_t primitiveIndex = primitiveBuffer.getPrimitiveIndex(*task.primitive);
656+
std::int32_t &dstData = primitiveBuffer.mappedData[primitiveIndex].materialIndex;
655657

656-
auto* const vkAssetExtended = dynamic_cast<vulkan::gltf::AssetExtended*>(assetExtended.get());
657-
658-
const bool isPrimitiveBufferHostVisible = vku::contains(
659-
gpu.allocator.getAllocationMemoryProperties(vkAssetExtended->primitiveBuffer.allocation),
660-
vk::MemoryPropertyFlagBits::eHostVisible);
661-
for (const auto &[primitive, originalMaterialIndex] : assetExtended->originalMaterialIndexByPrimitive) {
662-
if (primitive->mappings.empty()) {
663-
// Primitive material has no variants.
664-
continue;
665-
}
658+
if (vku::contains(gpu.allocator.getAllocationMemoryProperties(primitiveBuffer.allocation), vk::MemoryPropertyFlagBits::eHostVisible)) {
659+
gpu.device.waitIdle();
666660

667-
std::size_t materialIndex;
668-
if (const auto &i = primitive->mappings.at(task.variantIndex)) {
669-
materialIndex = *i;
661+
if (task.primitive->materialIndex) {
662+
dstData = *task.primitive->materialIndex + 1;
670663
}
671664
else {
672-
materialIndex = originalMaterialIndex.value();;
673-
}
674-
primitive->materialIndex.emplace(materialIndex);
675-
676-
const std::size_t primitiveIndex = vkAssetExtended->primitiveBuffer.getPrimitiveIndex(*primitive);
677-
std::int32_t &dstData = vkAssetExtended->primitiveBuffer.mappedData[primitiveIndex].materialIndex;
678-
if (isPrimitiveBufferHostVisible) {
679-
dstData = materialIndex + 1;
665+
dstData = 0;
680666
}
681-
else {
682-
const vk::DeviceSize dstOffset
683-
= reinterpret_cast<const std::byte*>(&dstData)
684-
- reinterpret_cast<const std::byte*>(vkAssetExtended->primitiveBuffer.mappedData.data());
685-
sharedDataUpdateCommandBuffer.updateBuffer<std::remove_cvref_t<decltype(dstData)>>(
686-
vkAssetExtended->primitiveBuffer, dstOffset, materialIndex + 1);
687-
hasUpdateData = true;
667+
}
668+
else {
669+
const vk::DeviceSize dstOffset
670+
= reinterpret_cast<const std::byte*>(&dstData)
671+
- reinterpret_cast<const std::byte*>(primitiveBuffer.mappedData.data());
672+
673+
std::uint32_t data = 0;
674+
if (task.primitive->materialIndex) {
675+
data = *task.primitive->materialIndex + 1;
688676
}
677+
678+
sharedDataUpdateCommandBuffer.updateBuffer<std::remove_cvref_t<decltype(dstData)>>(
679+
primitiveBuffer, dstOffset, data);
680+
hasUpdateData = true;
689681
}
690682
},
691683
[&](const control::task::MorphTargetWeightChanged &task) {

impl/control/ImGuiTaskCollector.cpp

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module vk_gltf_viewer.imgui.TaskCollector;
1111
import imgui.math;
1212

1313
import vk_gltf_viewer.global;
14+
import vk_gltf_viewer.gltf.util;
1415
import vk_gltf_viewer.gui.dialog;
1516
import vk_gltf_viewer.helpers.concepts;
1617
import vk_gltf_viewer.helpers.fastgltf;
@@ -1013,10 +1014,28 @@ void vk_gltf_viewer::control::ImGuiTaskCollector::materialVariants(gltf::AssetEx
10131014
selected = static_cast<int>(*assetExtended.imGuiSelectedMaterialVariantsIndex);
10141015
}
10151016

1016-
for (const auto &[i, variantName] : assetExtended.asset.materialVariants | ranges::views::enumerate) {
1017-
if (ImGui::RadioButton(variantName.c_str(), &selected, i)) {
1017+
for (const auto &[variantIndex, variantName] : assetExtended.asset.materialVariants | ranges::views::enumerate) {
1018+
if (ImGui::RadioButton(variantName.c_str(), &selected, variantIndex)) {
1019+
// Apply material variants.
1020+
for (fastgltf::Mesh &mesh : assetExtended.asset.meshes) {
1021+
for (fastgltf::Primitive &primitive : mesh.primitives) {
1022+
if (primitive.mappings.empty()) {
1023+
// Primitive is not affected by KHR_materials_variants.
1024+
continue;
1025+
}
1026+
1027+
if (const auto &variantMaterialIndex = primitive.mappings.at(variantIndex)) {
1028+
primitive.materialIndex.emplace(*variantMaterialIndex);
1029+
}
1030+
else {
1031+
const auto &originalMaterialIndex = assetExtended.originalMaterialIndexByPrimitive.at(&primitive);
1032+
primitive.materialIndex.emplace(originalMaterialIndex.value());
1033+
}
1034+
1035+
tasks.emplace(std::in_place_type<task::PrimitiveMaterialChanged>, &primitive);
1036+
}
1037+
}
10181038
assetExtended.imGuiSelectedMaterialVariantsIndex.emplace(selected);
1019-
tasks.emplace(std::in_place_type<task::SelectMaterialVariants>, i);
10201039
}
10211040
}
10221041
}
@@ -1406,20 +1425,69 @@ void vk_gltf_viewer::control::ImGuiTaskCollector::nodeInspector(gltf::AssetExten
14061425
for (auto &&[primitiveIndex, primitive]: mesh.primitives | ranges::views::enumerate) {
14071426
if (ImGui::CollapsingHeader(tempStringBuffer.write("Primitive {}", primitiveIndex).view().c_str())) {
14081427
ImGui::LabelText("Type", "%s", to_string(primitive.type).c_str());
1428+
1429+
bool primitiveMaterialChanged = false;
1430+
1431+
const char* previewValue = "-";
14091432
if (primitive.materialIndex) {
1410-
ImGui::WithID(*primitive.materialIndex, [&]() {
1411-
ImGui::WithLabel("Material"sv, [&]() {
1412-
if (ImGui::TextLink(getDisplayName(assetExtended.asset, assetExtended.asset.materials[*primitive.materialIndex]).c_str())) {
1413-
makeWindowVisible("Material Editor");
1414-
assetExtended.imGuiSelectedMaterialIndex.emplace(*primitive.materialIndex);
1415-
}
1433+
previewValue = getDisplayName(assetExtended.asset, assetExtended.asset.materials[*primitive.materialIndex]).c_str();
1434+
}
1435+
1436+
if (ImGui::BeginCombo("Material", previewValue)) {
1437+
if (ImGui::Selectable("-", !primitive.materialIndex) && primitive.materialIndex) {
1438+
// Unassign material.
1439+
primitive.materialIndex.reset();
1440+
primitiveMaterialChanged = true;
1441+
}
1442+
1443+
for (const auto &[materialIndex, material] : assetExtended.asset.materials | ranges::views::enumerate) {
1444+
ImGui::WithID(materialIndex, [&] {
1445+
ImGui::WithDisabled([&] {
1446+
if (ImGui::Selectable(getDisplayName(assetExtended.asset, material).c_str(), primitive.materialIndex == materialIndex) &&
1447+
primitive.materialIndex != materialIndex) {
1448+
primitive.materialIndex.emplace(materialIndex);
1449+
primitiveMaterialChanged = true;
1450+
}
1451+
}, !gltf::isMaterialCompatible(material, primitive));
14161452
});
1417-
});
1453+
}
1454+
1455+
ImGui::EndCombo();
14181456
}
1419-
else {
1420-
ImGui::WithDisabled([]() {
1421-
ImGui::LabelText("Material", "-");
1422-
});
1457+
1458+
const auto &originalMaterialIndex = assetExtended.originalMaterialIndexByPrimitive.at(&primitive);
1459+
if (to_optional(primitive.materialIndex) != originalMaterialIndex) {
1460+
ImGui::SameLine();
1461+
if (ImGui::SmallButton("Reset")) {
1462+
if (originalMaterialIndex) {
1463+
primitive.materialIndex.emplace(*originalMaterialIndex);
1464+
}
1465+
else {
1466+
primitive.materialIndex.reset();
1467+
}
1468+
primitiveMaterialChanged = true;
1469+
}
1470+
}
1471+
1472+
if (primitiveMaterialChanged) {
1473+
if (!primitive.mappings.empty()) {
1474+
// If primitive is affected by KHR_materials_variants, current active material variant
1475+
// index needed to be recalculated.
1476+
assetExtended.imGuiSelectedMaterialVariantsIndex = gltf::getActiveMaterialVariantIndex(
1477+
assetExtended.asset,
1478+
[&](const fastgltf::Primitive &primitive) {
1479+
return assetExtended.originalMaterialIndexByPrimitive.at(&primitive);
1480+
});
1481+
}
1482+
1483+
// Assign assetExtended.imGuiSelectedMaterialIndex as primitive material index (maybe nullopt).
1484+
if ((assetExtended.imGuiSelectedMaterialIndex = primitive.materialIndex)) {
1485+
// If assigned material is not nullopt, open the material editor.
1486+
// It will show the assigned material.
1487+
makeWindowVisible("Material Editor");
1488+
}
1489+
1490+
tasks.emplace(std::in_place_type<task::PrimitiveMaterialChanged>, &primitive);
14231491
}
14241492

14251493
attributeTable(

interface/control/Task.cppm

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export module vk_gltf_viewer.control.Task;
22

33
import std;
4+
export import fastgltf;
45
export import glm;
56
export import imgui.internal;
67

@@ -58,7 +59,7 @@ namespace vk_gltf_viewer::control {
5859
std::size_t materialIndex;
5960
Property property;
6061
};
61-
struct SelectMaterialVariants { std::size_t variantIndex; };
62+
struct PrimitiveMaterialChanged { const fastgltf::Primitive *primitive; };
6263
struct MorphTargetWeightChanged { std::size_t nodeIndex; std::size_t targetWeightStartIndex; std::size_t targetWeightCount; };
6364
struct BloomModeChanged{};
6465
}
@@ -84,7 +85,7 @@ namespace vk_gltf_viewer::control {
8485
task::NodeLocalTransformChanged,
8586
task::NodeWorldTransformChanged,
8687
task::MaterialPropertyChanged,
87-
task::SelectMaterialVariants,
88+
task::PrimitiveMaterialChanged,
8889
task::MorphTargetWeightChanged,
8990
task::BloomModeChanged>;
9091
}

interface/gltf/AssetExtended.cppm

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export import vk_gltf_viewer.gltf.AssetExternalBuffers;
99
export import vk_gltf_viewer.gltf.TextureUsage;
1010
export import vk_gltf_viewer.gltf.SceneInverseHierarchy;
1111
export import vk_gltf_viewer.gltf.StateCachedNodeVisibilityStructure;
12+
import vk_gltf_viewer.gltf.util;
1213
export import vk_gltf_viewer.imgui.ColorSpaceAndUsageCorrectedTextures;
1314
import vk_gltf_viewer.helpers.fastgltf;
1415
import vk_gltf_viewer.helpers.functional;
@@ -35,9 +36,9 @@ namespace vk_gltf_viewer::gltf {
3536
fastgltf::Asset asset;
3637

3738
/**
38-
* @brief Pairs of (primitive, original material index).
39+
* @brief Association of primitive -> original material index.
3940
*/
40-
std::vector<std::pair<fastgltf::Primitive*, std::optional<std::size_t>>> originalMaterialIndexByPrimitive;
41+
std::unordered_map<const fastgltf::Primitive*, std::optional<std::size_t>> originalMaterialIndexByPrimitive;
4142

4243
/**
4344
* @brief Map of (material index, texture usage flags) for each texture.
@@ -165,7 +166,7 @@ vk_gltf_viewer::gltf::AssetExtended::AssetExtended(const std::filesystem::path &
165166
// originalMaterialIndexByPrimitive
166167
for (fastgltf::Mesh &mesh: asset.meshes) {
167168
for (fastgltf::Primitive &primitive: mesh.primitives) {
168-
originalMaterialIndexByPrimitive.emplace_back(&primitive, primitive.materialIndex);
169+
originalMaterialIndexByPrimitive.try_emplace(&primitive, primitive.materialIndex);
169170
}
170171
}
171172

@@ -209,31 +210,9 @@ vk_gltf_viewer::gltf::AssetExtended::AssetExtended(const std::filesystem::path &
209210

210211
// imGuiSelectedMaterialVariantsIndex
211212
if (ranges::contains(asset.extensionsUsed, "KHR_materials_variants"sv)) {
212-
// Find primitives that are affected by KHR_material_variants.
213-
const auto isMaterialVariantUsed = [&](std::size_t variantIndex) {
214-
for (const fastgltf::Primitive *primitive : originalMaterialIndexByPrimitive | std::views::keys) {
215-
if (primitive->mappings.empty()) {
216-
// Primitive is not affected by KHR_material_variants.
217-
continue;
218-
}
219-
220-
const auto &materialIndex = primitive->mappings.at(variantIndex);
221-
if (materialIndex && *materialIndex != primitive->materialIndex.value()) {
222-
// The material variants given by variantIndex refers different material than what presented at
223-
// the loading time.
224-
return false;
225-
}
226-
}
227-
228-
return true;
229-
};
230-
231-
for (std::size_t variantIndex : ranges::views::upto(asset.materialVariants.size())) {
232-
if (isMaterialVariantUsed(variantIndex)) {
233-
imGuiSelectedMaterialVariantsIndex.emplace(variantIndex);
234-
break;
235-
}
236-
}
213+
imGuiSelectedMaterialVariantsIndex = getActiveMaterialVariantIndex(asset, [this](const fastgltf::Primitive &primitive) {
214+
return originalMaterialIndexByPrimitive.at(&primitive);
215+
});
237216
}
238217

239218
// nodeWorldTransforms

0 commit comments

Comments
 (0)