Scene hierarchy: group composition arcs into Reference/Payload submenus + add Payload ops
- Prim context menu: consolidate Add/Replace/Remove Reference under a single Reference▶ submenu - Prim context menu: add Payload▶ submenu with Add.../Remove▶/Clear All - Stage blank-area context menu: same grouping (Reference▶ and Payload▶) - Include pxr/usd/usd/payloads.h and pxr/usd/sdf/payload.h; use SdfPayloadEditorProxy + UsdPrimCompositionQuery::ArcTypeFilter::Payload for per-payload removal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+121
-27
@@ -8,7 +8,9 @@
|
||||
#include <pxr/usd/usd/prim.h>
|
||||
#include <pxr/usd/usd/primRange.h>
|
||||
#include <pxr/usd/usd/references.h>
|
||||
#include <pxr/usd/usd/payloads.h>
|
||||
#include <pxr/usd/usd/primCompositionQuery.h>
|
||||
#include <pxr/usd/sdf/payload.h>
|
||||
#include <pxr/usd/usdGeom/imageable.h>
|
||||
#include <pxr/usd/usdGeom/metrics.h>
|
||||
#include <pxr/usd/usdGeom/tokens.h>
|
||||
@@ -315,23 +317,21 @@ void SceneHierarchyPanel::Render() {
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// ---- Add Reference ----
|
||||
if (ImGui::MenuItem("Add Reference...")) {
|
||||
// ---- Reference submenu ----
|
||||
if (ImGui::BeginMenu("Reference")) {
|
||||
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 Reference File");
|
||||
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 xformName = SanitizeUsdName(stem);
|
||||
if (xformName.empty()) xformName = "Reference";
|
||||
|
||||
// Avoid name collision: append _N if the path already exists.
|
||||
std::string finalName = xformName;
|
||||
int suffix = 1;
|
||||
while (m_stage->GetPrimAtPath(SdfPath("/" + finalName)).IsValid()) {
|
||||
while (m_stage->GetPrimAtPath(SdfPath("/" + finalName)).IsValid())
|
||||
finalName = xformName + "_" + std::to_string(suffix++);
|
||||
}
|
||||
|
||||
SdfPath xformPath("/" + finalName);
|
||||
if (m_commandHistory) {
|
||||
@@ -342,11 +342,10 @@ void SceneHierarchyPanel::Render() {
|
||||
UsdPrim xformPrim = m_stage->DefinePrim(xformPath, TfToken("Xform"));
|
||||
if (xformPrim.IsValid()) {
|
||||
bool ok = xformPrim.GetReferences().AddReference(filePath);
|
||||
if (ok) {
|
||||
if (ok)
|
||||
LOG_INFO("Added reference '" + filePath + "' under prim: " + xformPath.GetString());
|
||||
} else {
|
||||
else
|
||||
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + xformPath.GetString());
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Failed to define Xform prim: " + xformPath.GetString());
|
||||
}
|
||||
@@ -356,6 +355,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();
|
||||
}
|
||||
@@ -620,29 +657,30 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// ---- Reference operations ----
|
||||
if (ImGui::MenuItem("Add Reference...")) {
|
||||
bool hasRefs = prim.HasAuthoredReferences();
|
||||
bool hasPayloads = prim.HasAuthoredPayloads();
|
||||
|
||||
// ---- Reference submenu ----
|
||||
if (ImGui::BeginMenu("Reference")) {
|
||||
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 Reference File");
|
||||
if (!filePath.empty()) {
|
||||
try {
|
||||
bool ok = prim.GetReferences().AddReference(filePath);
|
||||
if (ok) {
|
||||
if (ok)
|
||||
LOG_INFO("Added reference '" + filePath + "' to prim: " + prim.GetPath().GetString());
|
||||
} else {
|
||||
else
|
||||
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + prim.GetPath().GetString());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Add reference error: ") + e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
bool hasRefs = prim.HasAuthoredReferences();
|
||||
|
||||
// ---- Replace Reference ----
|
||||
if (ImGui::BeginMenu("Replace Reference", hasRefs)) {
|
||||
if (ImGui::BeginMenu("Replace", hasRefs)) {
|
||||
UsdPrimCompositionQuery::Filter replFilter;
|
||||
replFilter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
||||
replFilter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||
@@ -657,7 +695,6 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
||||
? "(internal reference)"
|
||||
: oldRef.GetAssetPath();
|
||||
if (ImGui::MenuItem(label.c_str())) {
|
||||
// NOTE: file dialog is blocking — close popup first via deferred path.
|
||||
m_pendingReplaceRef = oldRef;
|
||||
m_pendingReplaceRefPrim = prim.GetPath();
|
||||
m_doReplaceRefPick = true;
|
||||
@@ -665,15 +702,12 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
||||
anyRepl = true;
|
||||
}
|
||||
}
|
||||
if (!anyRepl) {
|
||||
if (!anyRepl)
|
||||
ImGui::TextDisabled("(no direct references)");
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
// ---- Remove Reference ----
|
||||
if (ImGui::BeginMenu("Remove Reference", hasRefs)) {
|
||||
// Collect direct reference arcs via composition query.
|
||||
if (ImGui::BeginMenu("Remove", hasRefs)) {
|
||||
UsdPrimCompositionQuery::Filter filter;
|
||||
filter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
||||
filter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||
@@ -698,10 +732,8 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
||||
anyListed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyListed) ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Clear All References")) {
|
||||
if (ImGui::MenuItem("Clear All")) {
|
||||
try {
|
||||
prim.GetReferences().ClearReferences();
|
||||
LOG_INFO("Cleared all references on: " + prim.GetPath().GetString());
|
||||
@@ -709,6 +741,68 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
||||
LOG_ERROR(std::string("Clear references error: ") + e.what());
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user