Compare commits
3 Commits
f56f4674cc
...
d280136ca7
| 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() };
|
||||
bool loaded = false;
|
||||
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) {
|
||||
LOG_INFO(std::string("Loaded Inter font from ") + p);
|
||||
loaded = true;
|
||||
|
||||
+473
-100
@@ -8,7 +8,12 @@
|
||||
#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/inherits.h>
|
||||
#include <pxr/usd/usd/specializes.h>
|
||||
#include <pxr/usd/usd/variantSets.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,46 +320,81 @@ void SceneHierarchyPanel::Render() {
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// ---- Add Reference ----
|
||||
if (ImGui::MenuItem("Add Reference...")) {
|
||||
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";
|
||||
// ---- 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()) {
|
||||
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()) {
|
||||
finalName = xformName + "_" + std::to_string(suffix++);
|
||||
std::string finalName = xformName;
|
||||
int suffix = 1;
|
||||
while (m_stage->GetPrimAtPath(SdfPath("/" + finalName)).IsValid())
|
||||
finalName = xformName + "_" + std::to_string(suffix++);
|
||||
|
||||
SdfPath xformPath("/" + finalName);
|
||||
if (m_commandHistory) {
|
||||
m_commandHistory->Push(std::make_unique<AddReferenceCommand>(
|
||||
m_stage, xformPath, filePath));
|
||||
} else {
|
||||
try {
|
||||
UsdPrim xformPrim = m_stage->DefinePrim(xformPath, TfToken("Xform"));
|
||||
if (xformPrim.IsValid()) {
|
||||
bool ok = xformPrim.GetReferences().AddReference(filePath);
|
||||
if (ok)
|
||||
LOG_INFO("Added reference '" + filePath + "' under prim: " + xformPath.GetString());
|
||||
else
|
||||
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + xformPath.GetString());
|
||||
} else {
|
||||
LOG_ERROR("Failed to define Xform prim: " + xformPath.GetString());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Add reference error: ") + e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
SdfPath xformPath("/" + finalName);
|
||||
if (m_commandHistory) {
|
||||
m_commandHistory->Push(std::make_unique<AddReferenceCommand>(
|
||||
m_stage, xformPath, filePath));
|
||||
} else {
|
||||
// ---- 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.GetReferences().AddReference(filePath);
|
||||
if (ok) {
|
||||
LOG_INFO("Added reference '" + filePath + "' under prim: " + xformPath.GetString());
|
||||
} else {
|
||||
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + xformPath.GetString());
|
||||
}
|
||||
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 reference error: ") + e.what());
|
||||
LOG_ERROR(std::string("Add payload error: ") + e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
@@ -363,6 +403,9 @@ void SceneHierarchyPanel::Render() {
|
||||
// Deferred confirm modal for prim removal (must be opened outside any popup stack).
|
||||
RenderRemovePrimModal();
|
||||
|
||||
// Deferred modal for arc operations (Inherit / Specialize / VariantSet / Variant).
|
||||
RenderArcModal();
|
||||
|
||||
// Deferred file-dialog for reference replacement (must run outside any popup stack).
|
||||
ProcessPendingReplaceRef();
|
||||
}
|
||||
@@ -620,96 +663,283 @@ void SceneHierarchyPanel::RenderContextMenu(const UsdPrim& prim) {
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// ---- Reference operations ----
|
||||
if (ImGui::MenuItem("Add Reference...")) {
|
||||
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) {
|
||||
LOG_INFO("Added reference '" + filePath + "' to prim: " + prim.GetPath().GetString());
|
||||
} else {
|
||||
LOG_ERROR("Failed to add reference '" + filePath + "' to: " + prim.GetPath().GetString());
|
||||
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)
|
||||
LOG_INFO("Added reference '" + filePath + "' to prim: " + prim.GetPath().GetString());
|
||||
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());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Add reference error: ") + e.what());
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginMenu("Replace", hasRefs)) {
|
||||
UsdPrimCompositionQuery::Filter replFilter;
|
||||
replFilter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
||||
replFilter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||
UsdPrimCompositionQuery replQuery(prim, replFilter);
|
||||
|
||||
bool anyRepl = false;
|
||||
for (auto& arc : replQuery.GetCompositionArcs()) {
|
||||
SdfReferenceEditorProxy editor;
|
||||
SdfReference oldRef;
|
||||
if (arc.GetIntroducingListEditor(&editor, &oldRef)) {
|
||||
std::string label = oldRef.GetAssetPath().empty()
|
||||
? "(internal reference)"
|
||||
: oldRef.GetAssetPath();
|
||||
if (ImGui::MenuItem(label.c_str())) {
|
||||
m_pendingReplaceRef = oldRef;
|
||||
m_pendingReplaceRefPrim = prim.GetPath();
|
||||
m_doReplaceRefPick = true;
|
||||
}
|
||||
anyRepl = true;
|
||||
}
|
||||
}
|
||||
if (!anyRepl)
|
||||
ImGui::TextDisabled("(no direct references)");
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Remove", hasRefs)) {
|
||||
UsdPrimCompositionQuery::Filter filter;
|
||||
filter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
||||
filter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||
UsdPrimCompositionQuery query(prim, filter);
|
||||
|
||||
bool anyListed = false;
|
||||
for (auto& arc : query.GetCompositionArcs()) {
|
||||
SdfReferenceEditorProxy editor;
|
||||
SdfReference ref;
|
||||
if (arc.GetIntroducingListEditor(&editor, &ref)) {
|
||||
std::string label = ref.GetAssetPath().empty()
|
||||
? "(internal reference)"
|
||||
: ref.GetAssetPath();
|
||||
if (ImGui::MenuItem(label.c_str())) {
|
||||
try {
|
||||
prim.GetReferences().RemoveReference(ref);
|
||||
LOG_INFO("Removed reference '" + label + "' from: " + prim.GetPath().GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Remove reference error: ") + e.what());
|
||||
}
|
||||
}
|
||||
anyListed = true;
|
||||
}
|
||||
}
|
||||
if (anyListed) ImGui::Separator();
|
||||
if (ImGui::MenuItem("Clear All")) {
|
||||
try {
|
||||
prim.GetReferences().ClearReferences();
|
||||
LOG_INFO("Cleared all references on: " + prim.GetPath().GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Clear references error: ") + e.what());
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
bool hasRefs = prim.HasAuthoredReferences();
|
||||
|
||||
// ---- Replace Reference ----
|
||||
if (ImGui::BeginMenu("Replace Reference", hasRefs)) {
|
||||
UsdPrimCompositionQuery::Filter replFilter;
|
||||
replFilter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
||||
replFilter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||
UsdPrimCompositionQuery replQuery(prim, replFilter);
|
||||
|
||||
bool anyRepl = false;
|
||||
for (auto& arc : replQuery.GetCompositionArcs()) {
|
||||
SdfReferenceEditorProxy editor;
|
||||
SdfReference oldRef;
|
||||
if (arc.GetIntroducingListEditor(&editor, &oldRef)) {
|
||||
std::string label = oldRef.GetAssetPath().empty()
|
||||
? "(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;
|
||||
// ---- 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());
|
||||
}
|
||||
anyRepl = true;
|
||||
}
|
||||
}
|
||||
if (!anyRepl) {
|
||||
ImGui::TextDisabled("(no direct references)");
|
||||
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();
|
||||
}
|
||||
|
||||
// ---- Remove Reference ----
|
||||
if (ImGui::BeginMenu("Remove Reference", hasRefs)) {
|
||||
// Collect direct reference arcs via composition query.
|
||||
UsdPrimCompositionQuery::Filter filter;
|
||||
filter.arcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter::Reference;
|
||||
filter.dependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter::Direct;
|
||||
UsdPrimCompositionQuery query(prim, filter);
|
||||
// ---- 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 anyListed = false;
|
||||
for (auto& arc : query.GetCompositionArcs()) {
|
||||
SdfReferenceEditorProxy editor;
|
||||
SdfReference ref;
|
||||
if (arc.GetIntroducingListEditor(&editor, &ref)) {
|
||||
std::string label = ref.GetAssetPath().empty()
|
||||
? "(internal reference)"
|
||||
: ref.GetAssetPath();
|
||||
if (ImGui::MenuItem(label.c_str())) {
|
||||
try {
|
||||
prim.GetReferences().RemoveReference(ref);
|
||||
LOG_INFO("Removed reference '" + label + "' from: " + prim.GetPath().GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Remove reference error: ") + e.what());
|
||||
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();
|
||||
}
|
||||
anyListed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyListed) ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Clear All References")) {
|
||||
try {
|
||||
prim.GetReferences().ClearReferences();
|
||||
LOG_INFO("Cleared all references on: " + prim.GetPath().GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Clear references error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
if (!m_doReplaceRefPick) return;
|
||||
m_doReplaceRefPick = false;
|
||||
|
||||
@@ -66,12 +66,15 @@ public:
|
||||
using StageMetadataChangedCallback = std::function<void()>;
|
||||
void SetOnStageMetadataChanged(StageMetadataChangedCallback callback) { m_onStageMetadataChanged = callback; }
|
||||
|
||||
enum class ArcModalMode { None, Inherit, Specialize, VariantSet, Variant };
|
||||
|
||||
private:
|
||||
void RenderPrimNode(const UsdPrim& prim);
|
||||
const char* GetPrimTypeIcon(const UsdPrim& prim) const;
|
||||
Icon GetPrimTypeIconEnum(const UsdPrim& prim) const;
|
||||
void RenderContextMenu(const UsdPrim& prim);
|
||||
void RenderRemovePrimModal();
|
||||
void RenderArcModal();
|
||||
void ProcessPendingReplaceRef();
|
||||
|
||||
PropertyManager* m_propertyManager;
|
||||
@@ -104,6 +107,13 @@ private:
|
||||
bool m_doReplaceRefPick = false;
|
||||
SdfPath m_pendingReplaceRefPrim;
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user