Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d280136ca7 | |||
| 81d4372bf6 | |||
| 3ba00761c1 |
@@ -106,7 +106,7 @@ bool ImGuiContext::Initialize(const std::string& windowTitle, int width, int hei
|
|||||||
const char* tryPaths[] = { fontPath0.c_str(), fontPath1.c_str() };
|
const char* tryPaths[] = { fontPath0.c_str(), fontPath1.c_str() };
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
for (const char* p : tryPaths) {
|
for (const char* p : tryPaths) {
|
||||||
ImFont* f = io.Fonts->AddFontFromFileTTF(p, 16.0f, &fontConfig);
|
ImFont* f = io.Fonts->AddFontFromFileTTF(p, 15.0f, &fontConfig);
|
||||||
if (f) {
|
if (f) {
|
||||||
LOG_INFO(std::string("Loaded Inter font from ") + p);
|
LOG_INFO(std::string("Loaded Inter font from ") + p);
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
|||||||
+400
-27
@@ -8,7 +8,12 @@
|
|||||||
#include <pxr/usd/usd/prim.h>
|
#include <pxr/usd/usd/prim.h>
|
||||||
#include <pxr/usd/usd/primRange.h>
|
#include <pxr/usd/usd/primRange.h>
|
||||||
#include <pxr/usd/usd/references.h>
|
#include <pxr/usd/usd/references.h>
|
||||||
|
#include <pxr/usd/usd/payloads.h>
|
||||||
|
#include <pxr/usd/usd/inherits.h>
|
||||||
|
#include <pxr/usd/usd/specializes.h>
|
||||||
|
#include <pxr/usd/usd/variantSets.h>
|
||||||
#include <pxr/usd/usd/primCompositionQuery.h>
|
#include <pxr/usd/usd/primCompositionQuery.h>
|
||||||
|
#include <pxr/usd/sdf/payload.h>
|
||||||
#include <pxr/usd/usdGeom/imageable.h>
|
#include <pxr/usd/usdGeom/imageable.h>
|
||||||
#include <pxr/usd/usdGeom/metrics.h>
|
#include <pxr/usd/usdGeom/metrics.h>
|
||||||
#include <pxr/usd/usdGeom/tokens.h>
|
#include <pxr/usd/usdGeom/tokens.h>
|
||||||
@@ -315,23 +320,21 @@ void SceneHierarchyPanel::Render() {
|
|||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
// ---- Add Reference ----
|
// ---- Reference submenu ----
|
||||||
if (ImGui::MenuItem("Add Reference...")) {
|
if (ImGui::BeginMenu("Reference")) {
|
||||||
|
if (ImGui::MenuItem("Add...")) {
|
||||||
std::string filePath = FileDialog::OpenFile(
|
std::string filePath = FileDialog::OpenFile(
|
||||||
"USD Files (*.usd;*.usda;*.usdc;*.usdz)\0*.usd;*.usda;*.usdc;*.usdz\0All Files (*.*)\0*.*\0",
|
"USD Files (*.usd;*.usda;*.usdc;*.usdz)\0*.usd;*.usda;*.usdc;*.usdz\0All Files (*.*)\0*.*\0",
|
||||||
"Add Reference File");
|
"Add Reference File");
|
||||||
if (!filePath.empty()) {
|
if (!filePath.empty()) {
|
||||||
// Derive a valid USD prim name from the file's stem.
|
|
||||||
std::string stem = std::filesystem::path(filePath).stem().string();
|
std::string stem = std::filesystem::path(filePath).stem().string();
|
||||||
std::string xformName = SanitizeUsdName(stem);
|
std::string xformName = SanitizeUsdName(stem);
|
||||||
if (xformName.empty()) xformName = "Reference";
|
if (xformName.empty()) xformName = "Reference";
|
||||||
|
|
||||||
// Avoid name collision: append _N if the path already exists.
|
|
||||||
std::string finalName = xformName;
|
std::string finalName = xformName;
|
||||||
int suffix = 1;
|
int suffix = 1;
|
||||||
while (m_stage->GetPrimAtPath(SdfPath("/" + finalName)).IsValid()) {
|
while (m_stage->GetPrimAtPath(SdfPath("/" + finalName)).IsValid())
|
||||||
finalName = xformName + "_" + std::to_string(suffix++);
|
finalName = xformName + "_" + std::to_string(suffix++);
|
||||||
}
|
|
||||||
|
|
||||||
SdfPath xformPath("/" + finalName);
|
SdfPath xformPath("/" + finalName);
|
||||||
if (m_commandHistory) {
|
if (m_commandHistory) {
|
||||||
@@ -342,11 +345,10 @@ void SceneHierarchyPanel::Render() {
|
|||||||
UsdPrim xformPrim = m_stage->DefinePrim(xformPath, TfToken("Xform"));
|
UsdPrim xformPrim = m_stage->DefinePrim(xformPath, TfToken("Xform"));
|
||||||
if (xformPrim.IsValid()) {
|
if (xformPrim.IsValid()) {
|
||||||
bool ok = xformPrim.GetReferences().AddReference(filePath);
|
bool ok = xformPrim.GetReferences().AddReference(filePath);
|
||||||
if (ok) {
|
if (ok)
|
||||||
LOG_INFO("Added reference '" + filePath + "' under prim: " + xformPath.GetString());
|
LOG_INFO("Added reference '" + filePath + "' under prim: " + xformPath.GetString());
|
||||||
} else {
|
else
|
||||||
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + xformPath.GetString());
|
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + xformPath.GetString());
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Failed to define Xform prim: " + xformPath.GetString());
|
LOG_ERROR("Failed to define Xform prim: " + xformPath.GetString());
|
||||||
}
|
}
|
||||||
@@ -356,6 +358,44 @@ void SceneHierarchyPanel::Render() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Payload submenu ----
|
||||||
|
if (ImGui::BeginMenu("Payload")) {
|
||||||
|
if (ImGui::MenuItem("Add...")) {
|
||||||
|
std::string filePath = FileDialog::OpenFile(
|
||||||
|
"USD Files (*.usd;*.usda;*.usdc;*.usdz)\0*.usd;*.usda;*.usdc;*.usdz\0All Files (*.*)\0*.*\0",
|
||||||
|
"Add Payload File");
|
||||||
|
if (!filePath.empty()) {
|
||||||
|
std::string stem = std::filesystem::path(filePath).stem().string();
|
||||||
|
std::string xformName = SanitizeUsdName(stem);
|
||||||
|
if (xformName.empty()) xformName = "Payload";
|
||||||
|
|
||||||
|
std::string finalName = xformName;
|
||||||
|
int suffix = 1;
|
||||||
|
while (m_stage->GetPrimAtPath(SdfPath("/" + finalName)).IsValid())
|
||||||
|
finalName = xformName + "_" + std::to_string(suffix++);
|
||||||
|
|
||||||
|
SdfPath xformPath("/" + finalName);
|
||||||
|
try {
|
||||||
|
UsdPrim xformPrim = m_stage->DefinePrim(xformPath, TfToken("Xform"));
|
||||||
|
if (xformPrim.IsValid()) {
|
||||||
|
bool ok = xformPrim.GetPayloads().AddPayload(filePath);
|
||||||
|
if (ok)
|
||||||
|
LOG_INFO("Added payload '" + filePath + "' under prim: " + xformPath.GetString());
|
||||||
|
else
|
||||||
|
LOG_ERROR("Failed to add payload '" + filePath + "' to: " + xformPath.GetString());
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Failed to define Xform prim: " + xformPath.GetString());
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Add payload error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
@@ -363,6 +403,9 @@ void SceneHierarchyPanel::Render() {
|
|||||||
// Deferred confirm modal for prim removal (must be opened outside any popup stack).
|
// Deferred confirm modal for prim removal (must be opened outside any popup stack).
|
||||||
RenderRemovePrimModal();
|
RenderRemovePrimModal();
|
||||||
|
|
||||||
|
// Deferred modal for arc operations (Inherit / Specialize / VariantSet / Variant).
|
||||||
|
RenderArcModal();
|
||||||
|
|
||||||
// Deferred file-dialog for reference replacement (must run outside any popup stack).
|
// Deferred file-dialog for reference replacement (must run outside any popup stack).
|
||||||
ProcessPendingReplaceRef();
|
ProcessPendingReplaceRef();
|
||||||
}
|
}
|
||||||
@@ -620,29 +663,30 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
|||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
// ---- Reference operations ----
|
bool hasRefs = prim.HasAuthoredReferences();
|
||||||
if (ImGui::MenuItem("Add Reference...")) {
|
bool hasPayloads = prim.HasAuthoredPayloads();
|
||||||
|
|
||||||
|
// ---- Reference submenu ----
|
||||||
|
if (ImGui::BeginMenu("Reference")) {
|
||||||
|
if (ImGui::MenuItem("Add...")) {
|
||||||
std::string filePath = FileDialog::OpenFile(
|
std::string filePath = FileDialog::OpenFile(
|
||||||
"USD Files (*.usd;*.usda;*.usdc;*.usdz)\0*.usd;*.usda;*.usdc;*.usdz\0All Files (*.*)\0*.*\0",
|
"USD Files (*.usd;*.usda;*.usdc;*.usdz)\0*.usd;*.usda;*.usdc;*.usdz\0All Files (*.*)\0*.*\0",
|
||||||
"Add Reference File");
|
"Add Reference File");
|
||||||
if (!filePath.empty()) {
|
if (!filePath.empty()) {
|
||||||
try {
|
try {
|
||||||
bool ok = prim.GetReferences().AddReference(filePath);
|
bool ok = prim.GetReferences().AddReference(filePath);
|
||||||
if (ok) {
|
if (ok)
|
||||||
LOG_INFO("Added reference '" + filePath + "' to prim: " + prim.GetPath().GetString());
|
LOG_INFO("Added reference '" + filePath + "' to prim: " + prim.GetPath().GetString());
|
||||||
} else {
|
else
|
||||||
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + prim.GetPath().GetString());
|
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + prim.GetPath().GetString());
|
||||||
}
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LOG_ERROR(std::string("Add reference error: ") + e.what());
|
LOG_ERROR(std::string("Add reference error: ") + e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
bool hasRefs = prim.HasAuthoredReferences();
|
if (ImGui::BeginMenu("Replace", hasRefs)) {
|
||||||
|
|
||||||
// ---- Replace Reference ----
|
|
||||||
if (ImGui::BeginMenu("Replace Reference", hasRefs)) {
|
|
||||||
UsdPrimCompositionQuery::Filter replFilter;
|
UsdPrimCompositionQuery::Filter replFilter;
|
||||||
replFilter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
replFilter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
||||||
replFilter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
replFilter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||||
@@ -657,7 +701,6 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
|||||||
? "(internal reference)"
|
? "(internal reference)"
|
||||||
: oldRef.GetAssetPath();
|
: oldRef.GetAssetPath();
|
||||||
if (ImGui::MenuItem(label.c_str())) {
|
if (ImGui::MenuItem(label.c_str())) {
|
||||||
// NOTE: file dialog is blocking — close popup first via deferred path.
|
|
||||||
m_pendingReplaceRef = oldRef;
|
m_pendingReplaceRef = oldRef;
|
||||||
m_pendingReplaceRefPrim = prim.GetPath();
|
m_pendingReplaceRefPrim = prim.GetPath();
|
||||||
m_doReplaceRefPick = true;
|
m_doReplaceRefPick = true;
|
||||||
@@ -665,15 +708,12 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
|||||||
anyRepl = true;
|
anyRepl = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!anyRepl) {
|
if (!anyRepl)
|
||||||
ImGui::TextDisabled("(no direct references)");
|
ImGui::TextDisabled("(no direct references)");
|
||||||
}
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Remove Reference ----
|
if (ImGui::BeginMenu("Remove", hasRefs)) {
|
||||||
if (ImGui::BeginMenu("Remove Reference", hasRefs)) {
|
|
||||||
// Collect direct reference arcs via composition query.
|
|
||||||
UsdPrimCompositionQuery::Filter filter;
|
UsdPrimCompositionQuery::Filter filter;
|
||||||
filter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
filter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
||||||
filter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
filter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||||
@@ -698,10 +738,8 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
|||||||
anyListed = true;
|
anyListed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anyListed) ImGui::Separator();
|
if (anyListed) ImGui::Separator();
|
||||||
|
if (ImGui::MenuItem("Clear All")) {
|
||||||
if (ImGui::MenuItem("Clear All References")) {
|
|
||||||
try {
|
try {
|
||||||
prim.GetReferences().ClearReferences();
|
prim.GetReferences().ClearReferences();
|
||||||
LOG_INFO("Cleared all references on: " + prim.GetPath().GetString());
|
LOG_INFO("Cleared all references on: " + prim.GetPath().GetString());
|
||||||
@@ -709,10 +747,202 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
|||||||
LOG_ERROR(std::string("Clear references error: ") + e.what());
|
LOG_ERROR(std::string("Clear references error: ") + e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Payload submenu ----
|
||||||
|
if (ImGui::BeginMenu("Payload")) {
|
||||||
|
if (ImGui::MenuItem("Add...")) {
|
||||||
|
std::string filePath = FileDialog::OpenFile(
|
||||||
|
"USD Files (*.usd;*.usda;*.usdc;*.usdz)\0*.usd;*.usda;*.usdc;*.usdz\0All Files (*.*)\0*.*\0",
|
||||||
|
"Add Payload File");
|
||||||
|
if (!filePath.empty()) {
|
||||||
|
try {
|
||||||
|
bool ok = prim.GetPayloads().AddPayload(filePath);
|
||||||
|
if (ok)
|
||||||
|
LOG_INFO("Added payload '" + filePath + "' to prim: " + prim.GetPath().GetString());
|
||||||
|
else
|
||||||
|
LOG_ERROR("Failed to add payload '" + filePath + "' to: " + prim.GetPath().GetString());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Add payload error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::BeginMenu("Remove", hasPayloads)) {
|
||||||
|
UsdPrimCompositionQuery::Filter filter;
|
||||||
|
filter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Payload;
|
||||||
|
filter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||||
|
UsdPrimCompositionQuery query(prim, filter);
|
||||||
|
|
||||||
|
bool anyListed = false;
|
||||||
|
for (auto& arc : query.GetCompositionArcs()) {
|
||||||
|
SdfPayloadEditorProxy editor;
|
||||||
|
SdfPayload payload;
|
||||||
|
if (arc.GetIntroducingListEditor(&editor, &payload)) {
|
||||||
|
std::string label = payload.GetAssetPath().empty()
|
||||||
|
? "(internal payload)"
|
||||||
|
: payload.GetAssetPath();
|
||||||
|
if (ImGui::MenuItem(label.c_str())) {
|
||||||
|
try {
|
||||||
|
prim.GetPayloads().RemovePayload(payload);
|
||||||
|
LOG_INFO("Removed payload '" + label + "' from: " + prim.GetPath().GetString());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Remove payload error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
anyListed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (anyListed) ImGui::Separator();
|
||||||
|
if (ImGui::MenuItem("Clear All")) {
|
||||||
|
try {
|
||||||
|
prim.GetPayloads().ClearPayloads();
|
||||||
|
LOG_INFO("Cleared all payloads on: " + prim.GetPath().GetString());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Clear payloads error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Inherit submenu ----
|
||||||
|
bool hasInherits = prim.HasAuthoredInherits();
|
||||||
|
if (ImGui::BeginMenu("Inherit")) {
|
||||||
|
if (ImGui::MenuItem("Add...")) {
|
||||||
|
m_arcModalMode = ArcModalMode::Inherit;
|
||||||
|
m_arcModalTargetPrim = prim.GetPath();
|
||||||
|
m_arcModalBuf[0] = '\0';
|
||||||
|
m_openArcModal = true;
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
if (ImGui::BeginMenu("Remove", hasInherits)) {
|
||||||
|
SdfPathVector paths = prim.GetInherits().GetAllDirectInherits();
|
||||||
|
bool any = false;
|
||||||
|
for (const SdfPath& ipath : paths) {
|
||||||
|
if (ImGui::MenuItem(ipath.GetText())) {
|
||||||
|
try {
|
||||||
|
prim.GetInherits().RemoveInherit(ipath);
|
||||||
|
LOG_INFO("Removed inherit '" + ipath.GetString() + "' from: " + prim.GetPath().GetString());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Remove inherit error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
if (any) ImGui::Separator();
|
||||||
|
if (ImGui::MenuItem("Clear All")) {
|
||||||
|
try {
|
||||||
|
prim.GetInherits().ClearInherits();
|
||||||
|
LOG_INFO("Cleared all inherits on: " + prim.GetPath().GetString());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Clear inherits error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Specialize submenu ----
|
||||||
|
bool hasSpecializes = prim.HasAuthoredSpecializes();
|
||||||
|
if (ImGui::BeginMenu("Specialize")) {
|
||||||
|
if (ImGui::MenuItem("Add...")) {
|
||||||
|
m_arcModalMode = ArcModalMode::Specialize;
|
||||||
|
m_arcModalTargetPrim = prim.GetPath();
|
||||||
|
m_arcModalBuf[0] = '\0';
|
||||||
|
m_openArcModal = true;
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
if (ImGui::BeginMenu("Remove", hasSpecializes)) {
|
||||||
|
UsdPrimCompositionQuery::Filter filter;
|
||||||
|
filter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Specialize;
|
||||||
|
filter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||||
|
UsdPrimCompositionQuery query(prim, filter);
|
||||||
|
|
||||||
|
bool any = false;
|
||||||
|
for (auto& arc : query.GetCompositionArcs()) {
|
||||||
|
SdfPathEditorProxy editor;
|
||||||
|
SdfPath specPath;
|
||||||
|
if (arc.GetIntroducingListEditor(&editor, &specPath)) {
|
||||||
|
if (ImGui::MenuItem(specPath.GetText())) {
|
||||||
|
try {
|
||||||
|
prim.GetSpecializes().RemoveSpecialize(specPath);
|
||||||
|
LOG_INFO("Removed specialize '" + specPath.GetString() + "' from: " + prim.GetPath().GetString());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Remove specialize error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (any) ImGui::Separator();
|
||||||
|
if (ImGui::MenuItem("Clear All")) {
|
||||||
|
try {
|
||||||
|
prim.GetSpecializes().ClearSpecializes();
|
||||||
|
LOG_INFO("Cleared all specializes on: " + prim.GetPath().GetString());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Clear specializes error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- VariantSet submenu ----
|
||||||
|
if (ImGui::BeginMenu("VariantSet")) {
|
||||||
|
if (ImGui::MenuItem("Add...")) {
|
||||||
|
m_arcModalMode = ArcModalMode::VariantSet;
|
||||||
|
m_arcModalTargetPrim = prim.GetPath();
|
||||||
|
m_arcModalBuf[0] = '\0';
|
||||||
|
m_openArcModal = true;
|
||||||
|
}
|
||||||
|
bool hasVarSets = prim.HasVariantSets();
|
||||||
|
if (hasVarSets) {
|
||||||
|
ImGui::Separator();
|
||||||
|
std::vector<std::string> vsNames;
|
||||||
|
prim.GetVariantSets().GetNames(&vsNames);
|
||||||
|
for (const std::string& vsName : vsNames) {
|
||||||
|
if (ImGui::BeginMenu(vsName.c_str())) {
|
||||||
|
if (ImGui::MenuItem("Add Variant...")) {
|
||||||
|
m_arcModalMode = ArcModalMode::Variant;
|
||||||
|
m_arcModalTargetPrim = prim.GetPath();
|
||||||
|
m_arcModalVarSetName = vsName;
|
||||||
|
m_arcModalBuf[0] = '\0';
|
||||||
|
m_openArcModal = true;
|
||||||
|
}
|
||||||
|
UsdVariantSet vs = prim.GetVariantSet(vsName);
|
||||||
|
std::vector<std::string> varNames = vs.GetVariantNames();
|
||||||
|
if (!varNames.empty()) {
|
||||||
|
ImGui::Separator();
|
||||||
|
std::string currentSel = vs.GetVariantSelection();
|
||||||
|
for (const std::string& varName : varNames) {
|
||||||
|
bool isCurrent = (varName == currentSel);
|
||||||
|
if (ImGui::MenuItem(varName.c_str(), nullptr, isCurrent)) {
|
||||||
|
try {
|
||||||
|
vs.SetVariantSelection(varName);
|
||||||
|
LOG_INFO("Set variant '" + varName + "' on '" + vsName + "' for: " + prim.GetPath().GetString());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Set variant selection error: ") + e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Prim removal ----
|
// ---- Prim removal ----
|
||||||
// Only show "Remove Prim" for prims that have a local spec authored in the root
|
// Only show "Remove Prim" for prims that have a local spec authored in the root
|
||||||
// layer. Prims brought in purely via composition from an external referenced
|
// layer. Prims brought in purely via composition from an external referenced
|
||||||
@@ -815,6 +1045,149 @@ void SceneHierarchyPanel::RenderRemovePrimModal() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SceneHierarchyPanel::RenderArcModal() {
|
||||||
|
if (m_openArcModal) {
|
||||||
|
ImGui::OpenPopup("Arc Operation##arc");
|
||||||
|
m_openArcModal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||||
|
ImGui::SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(420, 0), ImGuiCond_Always);
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupModal("Arc Operation##arc", nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) {
|
||||||
|
|
||||||
|
const char* title = "Arc Operation";
|
||||||
|
const char* prompt = "Path:";
|
||||||
|
bool isNameInput = false;
|
||||||
|
|
||||||
|
switch (m_arcModalMode) {
|
||||||
|
case ArcModalMode::Inherit:
|
||||||
|
title = "Add Inherit";
|
||||||
|
prompt = "Inherit path (e.g. /ClassName):";
|
||||||
|
break;
|
||||||
|
case ArcModalMode::Specialize:
|
||||||
|
title = "Add Specialize";
|
||||||
|
prompt = "Specialize path (e.g. /BasePrim):";
|
||||||
|
break;
|
||||||
|
case ArcModalMode::VariantSet:
|
||||||
|
title = "Add VariantSet";
|
||||||
|
prompt = "VariantSet name:";
|
||||||
|
isNameInput = true;
|
||||||
|
break;
|
||||||
|
case ArcModalMode::Variant:
|
||||||
|
title = "Add Variant";
|
||||||
|
prompt = "Variant name:";
|
||||||
|
isNameInput = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
ImGui::EndPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::TextUnformatted(title);
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextDisabled("Prim: %s", m_arcModalTargetPrim.GetText());
|
||||||
|
if (m_arcModalMode == ArcModalMode::Variant)
|
||||||
|
ImGui::TextDisabled("VariantSet: %s", m_arcModalVarSetName.c_str());
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextUnformatted(prompt);
|
||||||
|
ImGui::SetNextItemWidth(-1.0f);
|
||||||
|
if (ImGui::IsWindowAppearing())
|
||||||
|
ImGui::SetKeyboardFocusHere();
|
||||||
|
bool confirm = ImGui::InputText("##arcbuf", m_arcModalBuf,
|
||||||
|
sizeof(m_arcModalBuf),
|
||||||
|
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
bool inputNonEmpty = (m_arcModalBuf[0] != '\0');
|
||||||
|
float buttonWidth = 120.0f;
|
||||||
|
float spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||||
|
float totalW = buttonWidth * 2.0f + spacing;
|
||||||
|
ImGui::SetCursorPosX(
|
||||||
|
(ImGui::GetContentRegionAvail().x - totalW) * 0.5f + ImGui::GetCursorPosX());
|
||||||
|
|
||||||
|
// Capture Enter-confirm before BeginDisabled so it works regardless of button state.
|
||||||
|
bool doConfirm = (confirm && inputNonEmpty);
|
||||||
|
|
||||||
|
if (!inputNonEmpty) ImGui::BeginDisabled();
|
||||||
|
if (ImGui::Button("OK", ImVec2(buttonWidth, 0)))
|
||||||
|
doConfirm = inputNonEmpty;
|
||||||
|
if (!inputNonEmpty) ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button("Cancel", ImVec2(buttonWidth, 0))) {
|
||||||
|
m_arcModalMode = ArcModalMode::None;
|
||||||
|
m_arcModalTargetPrim = SdfPath();
|
||||||
|
m_arcModalVarSetName.clear();
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doConfirm) {
|
||||||
|
std::string raw(m_arcModalBuf);
|
||||||
|
std::string val = isNameInput ? SanitizeUsdName(raw) : raw;
|
||||||
|
|
||||||
|
if (m_stage && !m_arcModalTargetPrim.IsEmpty()) {
|
||||||
|
UsdPrim prim = m_stage->GetPrimAtPath(m_arcModalTargetPrim);
|
||||||
|
if (prim.IsValid()) {
|
||||||
|
try {
|
||||||
|
switch (m_arcModalMode) {
|
||||||
|
case ArcModalMode::Inherit: {
|
||||||
|
SdfPath ipath(val);
|
||||||
|
bool ok = prim.GetInherits().AddInherit(ipath);
|
||||||
|
if (ok)
|
||||||
|
LOG_INFO("Added inherit '" + val + "' to: " + m_arcModalTargetPrim.GetString());
|
||||||
|
else
|
||||||
|
LOG_ERROR("Failed to add inherit '" + val + "' to: " + m_arcModalTargetPrim.GetString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ArcModalMode::Specialize: {
|
||||||
|
SdfPath spath(val);
|
||||||
|
bool ok = prim.GetSpecializes().AddSpecialize(spath);
|
||||||
|
if (ok)
|
||||||
|
LOG_INFO("Added specialize '" + val + "' to: " + m_arcModalTargetPrim.GetString());
|
||||||
|
else
|
||||||
|
LOG_ERROR("Failed to add specialize '" + val + "' to: " + m_arcModalTargetPrim.GetString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ArcModalMode::VariantSet: {
|
||||||
|
prim.GetVariantSets().AddVariantSet(val);
|
||||||
|
LOG_INFO("Added variantSet '" + val + "' to: " + m_arcModalTargetPrim.GetString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ArcModalMode::Variant: {
|
||||||
|
UsdVariantSet vs = prim.GetVariantSet(m_arcModalVarSetName);
|
||||||
|
bool ok = vs.AddVariant(val);
|
||||||
|
if (ok)
|
||||||
|
LOG_INFO("Added variant '" + val + "' to variantSet '" + m_arcModalVarSetName + "' on: " + m_arcModalTargetPrim.GetString());
|
||||||
|
else
|
||||||
|
LOG_ERROR("Failed to add variant '" + val + "' to variantSet '" + m_arcModalVarSetName + "'");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(std::string("Arc operation error: ") + e.what());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Arc modal: prim no longer valid: " + m_arcModalTargetPrim.GetString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_arcModalMode = ArcModalMode::None;
|
||||||
|
m_arcModalTargetPrim = SdfPath();
|
||||||
|
m_arcModalVarSetName.clear();
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SceneHierarchyPanel::ProcessPendingReplaceRef() {
|
void SceneHierarchyPanel::ProcessPendingReplaceRef() {
|
||||||
if (!m_doReplaceRefPick) return;
|
if (!m_doReplaceRefPick) return;
|
||||||
m_doReplaceRefPick = false;
|
m_doReplaceRefPick = false;
|
||||||
|
|||||||
@@ -66,12 +66,15 @@ public:
|
|||||||
using StageMetadataChangedCallback = std::function<void()>;
|
using StageMetadataChangedCallback = std::function<void()>;
|
||||||
void SetOnStageMetadataChanged(StageMetadataChangedCallback callback) { m_onStageMetadataChanged = callback; }
|
void SetOnStageMetadataChanged(StageMetadataChangedCallback callback) { m_onStageMetadataChanged = callback; }
|
||||||
|
|
||||||
|
enum class ArcModalMode { None, Inherit, Specialize, VariantSet, Variant };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void RenderPrimNode(const UsdPrim& prim);
|
void RenderPrimNode(const UsdPrim& prim);
|
||||||
const char* GetPrimTypeIcon(const UsdPrim& prim) const;
|
const char* GetPrimTypeIcon(const UsdPrim& prim) const;
|
||||||
Icon GetPrimTypeIconEnum(const UsdPrim& prim) const;
|
Icon GetPrimTypeIconEnum(const UsdPrim& prim) const;
|
||||||
void RenderContextMenu(const UsdPrim& prim);
|
void RenderContextMenu(const UsdPrim& prim);
|
||||||
void RenderRemovePrimModal();
|
void RenderRemovePrimModal();
|
||||||
|
void RenderArcModal();
|
||||||
void ProcessPendingReplaceRef();
|
void ProcessPendingReplaceRef();
|
||||||
|
|
||||||
PropertyManager* m_propertyManager;
|
PropertyManager* m_propertyManager;
|
||||||
@@ -104,6 +107,13 @@ private:
|
|||||||
bool m_doReplaceRefPick = false;
|
bool m_doReplaceRefPick = false;
|
||||||
SdfPath m_pendingReplaceRefPrim;
|
SdfPath m_pendingReplaceRefPrim;
|
||||||
pxr::SdfReference m_pendingReplaceRef;
|
pxr::SdfReference m_pendingReplaceRef;
|
||||||
|
|
||||||
|
/// Arc modal state (Inherit / Specialize / VariantSet / Variant).
|
||||||
|
ArcModalMode m_arcModalMode = ArcModalMode::None;
|
||||||
|
SdfPath m_arcModalTargetPrim;
|
||||||
|
std::string m_arcModalVarSetName;
|
||||||
|
char m_arcModalBuf[512] = {};
|
||||||
|
bool m_openArcModal = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UsdLayerManager
|
} // namespace UsdLayerManager
|
||||||
|
|||||||
Reference in New Issue
Block a user