Init Repo
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
#include "CommandHistory.h"
|
||||
#include "../utils/Logger.h"
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
void CommandHistory::Push(std::unique_ptr<ICommand> cmd) {
|
||||
if (!cmd) return;
|
||||
try {
|
||||
cmd->Execute();
|
||||
} catch (...) {
|
||||
LOG_ERROR("CommandHistory::Push — Execute() threw an exception; command discarded");
|
||||
return;
|
||||
}
|
||||
m_undoStack.push_back(std::move(cmd));
|
||||
m_redoStack.clear();
|
||||
}
|
||||
|
||||
void CommandHistory::Undo() {
|
||||
if (m_undoStack.empty()) return;
|
||||
auto& cmd = m_undoStack.back();
|
||||
try {
|
||||
cmd->Undo();
|
||||
} catch (...) {
|
||||
LOG_ERROR("CommandHistory::Undo — Undo() threw an exception; history cleared for safety");
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
m_redoStack.push_back(std::move(cmd));
|
||||
m_undoStack.pop_back();
|
||||
}
|
||||
|
||||
void CommandHistory::Redo() {
|
||||
if (m_redoStack.empty()) return;
|
||||
auto& cmd = m_redoStack.back();
|
||||
try {
|
||||
cmd->Execute();
|
||||
} catch (...) {
|
||||
LOG_ERROR("CommandHistory::Redo — Execute() threw an exception; history cleared for safety");
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
m_undoStack.push_back(std::move(cmd));
|
||||
m_redoStack.pop_back();
|
||||
}
|
||||
|
||||
void CommandHistory::Clear() {
|
||||
m_undoStack.clear();
|
||||
m_redoStack.clear();
|
||||
}
|
||||
|
||||
std::string CommandHistory::GetUndoDescription() const {
|
||||
return m_undoStack.empty() ? std::string() : m_undoStack.back()->GetDescription();
|
||||
}
|
||||
|
||||
std::string CommandHistory::GetRedoDescription() const {
|
||||
return m_redoStack.empty() ? std::string() : m_redoStack.back()->GetDescription();
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
/// Pure-virtual interface for all reversible operations.
|
||||
class ICommand {
|
||||
public:
|
||||
virtual ~ICommand() = default;
|
||||
virtual void Execute() = 0;
|
||||
virtual void Undo() = 0;
|
||||
virtual std::string GetDescription() const = 0;
|
||||
};
|
||||
|
||||
/// Application-level undo/redo stack.
|
||||
///
|
||||
/// Push(cmd) executes the command, pushes it onto the undo stack,
|
||||
/// and clears the redo stack. Undo() pops from the undo stack, calls
|
||||
/// Undo() on the command, and pushes it onto the redo stack (and vice
|
||||
/// versa for Redo()).
|
||||
///
|
||||
/// Call Clear() whenever the USD stage is replaced so stale USD object
|
||||
/// references (UsdPrim, UsdAttribute, SdfLayerHandle) cannot be dereferenced.
|
||||
class CommandHistory {
|
||||
public:
|
||||
CommandHistory() = default;
|
||||
~CommandHistory() = default;
|
||||
|
||||
/// Execute cmd and push onto undo stack; clears redo stack.
|
||||
void Push(std::unique_ptr<ICommand> cmd);
|
||||
|
||||
/// Undo the top command (no-op if stack is empty).
|
||||
void Undo();
|
||||
|
||||
/// Redo the top undone command (no-op if stack is empty).
|
||||
void Redo();
|
||||
|
||||
/// Clear both stacks (must be called on stage close/open).
|
||||
void Clear();
|
||||
|
||||
bool CanUndo() const { return !m_undoStack.empty(); }
|
||||
bool CanRedo() const { return !m_redoStack.empty(); }
|
||||
|
||||
/// Description of the command that Undo() would reverse, or empty string.
|
||||
std::string GetUndoDescription() const;
|
||||
|
||||
/// Description of the command that Redo() would replay, or empty string.
|
||||
std::string GetRedoDescription() const;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ICommand>> m_undoStack;
|
||||
std::vector<std::unique_ptr<ICommand>> m_redoStack;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,215 @@
|
||||
#include "LayerManager.h"
|
||||
#include "../utils/Logger.h"
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <pxr/usd/sdf/layerUtils.h>
|
||||
#include <filesystem>
|
||||
|
||||
PXR_NAMESPACE_USING_DIRECTIVE
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
LayerManager::LayerManager()
|
||||
: m_stage(nullptr) {
|
||||
}
|
||||
|
||||
LayerManager::~LayerManager() {
|
||||
}
|
||||
|
||||
void LayerManager::SetStage(UsdStageRefPtr stage) {
|
||||
m_stage = stage;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void LayerManager::Refresh() {
|
||||
BuildLayerList();
|
||||
}
|
||||
|
||||
std::vector<LayerInfo> LayerManager::GetLayerStack() const {
|
||||
return m_layers;
|
||||
}
|
||||
|
||||
std::vector<LayerInfo> LayerManager::GetSublayers() const {
|
||||
std::vector<LayerInfo> sublayers;
|
||||
if (!m_stage) return sublayers;
|
||||
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) return sublayers;
|
||||
|
||||
for (const auto& info : m_layers) {
|
||||
if (!info.isRootLayer && !info.isSessionLayer) {
|
||||
sublayers.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
return sublayers;
|
||||
}
|
||||
|
||||
bool LayerManager::CreateSublayer(const std::string& identifier, int index) {
|
||||
if (!m_stage) {
|
||||
LOG_ERROR("No stage to create sublayer on");
|
||||
return false;
|
||||
}
|
||||
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) {
|
||||
LOG_ERROR("No root layer to add sublayer to");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
LOG_INFO("Creating sublayer: " + identifier);
|
||||
rootLayer->InsertSubLayerPath(identifier, index);
|
||||
Refresh();
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Failed to create sublayer: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LayerManager::InsertSublayerPath(const std::string& path, int index) {
|
||||
return CreateSublayer(path, index);
|
||||
}
|
||||
|
||||
bool LayerManager::RemoveSublayer(int index) {
|
||||
if (!m_stage) return false;
|
||||
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) return false;
|
||||
|
||||
try {
|
||||
SdfSubLayerProxy sublayers = rootLayer->GetSubLayerPaths();
|
||||
if (index < 0 || static_cast<size_t>(index) >= sublayers.size()) {
|
||||
LOG_ERROR("Sublayer index out of range");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Removing sublayer at index: " + std::to_string(index));
|
||||
sublayers.erase(sublayers.begin() + index);
|
||||
Refresh();
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Failed to remove sublayer: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LayerManager::MoveSublayerUp(int index) {
|
||||
if (index <= 0) return false;
|
||||
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) return false;
|
||||
|
||||
try {
|
||||
SdfSubLayerProxy sublayers = rootLayer->GetSubLayerPaths();
|
||||
if (static_cast<size_t>(index) >= sublayers.size()) return false;
|
||||
|
||||
std::string temp = sublayers[index];
|
||||
sublayers[index] = sublayers[index - 1];
|
||||
sublayers[index - 1] = temp;
|
||||
Refresh();
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Failed to move sublayer: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LayerManager::MoveSublayerDown(int index) {
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) return false;
|
||||
|
||||
try {
|
||||
SdfSubLayerProxy sublayers = rootLayer->GetSubLayerPaths();
|
||||
if (index < 0 || static_cast<size_t>(index + 1) >= sublayers.size()) return false;
|
||||
|
||||
std::string temp = sublayers[index];
|
||||
sublayers[index] = sublayers[index + 1];
|
||||
sublayers[index + 1] = temp;
|
||||
Refresh();
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Failed to move sublayer: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void LayerManager::MuteLayer(const std::string& layerIdentifier) {
|
||||
if (m_stage) {
|
||||
m_stage->MuteLayer(layerIdentifier);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void LayerManager::UnmuteLayer(const std::string& layerIdentifier) {
|
||||
if (m_stage) {
|
||||
m_stage->UnmuteLayer(layerIdentifier);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
bool LayerManager::IsLayerMuted(const std::string& layerIdentifier) const {
|
||||
if (m_stage) {
|
||||
return m_stage->IsLayerMuted(layerIdentifier);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string LayerManager::ExtractDisplayName(const std::string& identifier) {
|
||||
// Try to extract a user-friendly name from the identifier
|
||||
|
||||
// For file paths, use the filename
|
||||
std::filesystem::path path(identifier);
|
||||
if (path.has_filename()) {
|
||||
std::string filename = path.filename().string();
|
||||
if (!filename.empty()) {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
// For anonymous layers or other identifiers, return a short representation
|
||||
if (identifier.find("anon:") == 0) {
|
||||
return "Anonymous Layer";
|
||||
}
|
||||
|
||||
if (identifier.find("session:") == 0) {
|
||||
return "Session Layer";
|
||||
}
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
void LayerManager::BuildLayerList() {
|
||||
m_layers.clear();
|
||||
|
||||
if (!m_stage) return;
|
||||
|
||||
try {
|
||||
// Get the full layer stack (root + all sublayers)
|
||||
SdfLayerHandleVector layerStack = m_stage->GetLayerStack();
|
||||
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
SdfLayerHandle sessionLayer = m_stage->GetSessionLayer();
|
||||
|
||||
for (const auto& layer : layerStack) {
|
||||
LayerInfo info;
|
||||
info.layer = layer;
|
||||
info.identifier = layer->GetIdentifier();
|
||||
info.displayName = ExtractDisplayName(info.identifier);
|
||||
info.realPath = layer->GetRealPath();
|
||||
info.isMuted = layer->IsMuted();
|
||||
info.isAnonymous = layer->IsAnonymous();
|
||||
info.isRootLayer = (layer == rootLayer);
|
||||
info.isSessionLayer = (layer == sessionLayer);
|
||||
|
||||
m_layers.push_back(info);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Built layer list with " + std::to_string(m_layers.size()) + " layers");
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Failed to build layer list: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
struct LayerInfo {
|
||||
pxr::SdfLayerHandle layer;
|
||||
std::string identifier;
|
||||
std::string displayName;
|
||||
std::string realPath;
|
||||
bool isMuted;
|
||||
bool isAnonymous;
|
||||
bool isRootLayer;
|
||||
bool isSessionLayer;
|
||||
};
|
||||
|
||||
class LayerManager {
|
||||
public:
|
||||
LayerManager();
|
||||
~LayerManager();
|
||||
|
||||
void SetStage(pxr::UsdStageRefPtr stage);
|
||||
void Refresh();
|
||||
|
||||
// Layer information
|
||||
std::vector<LayerInfo> GetLayerStack() const;
|
||||
std::vector<LayerInfo> GetSublayers() const;
|
||||
int GetLayerCount() const { return static_cast<int>(m_layers.size()); }
|
||||
|
||||
// Layer operations
|
||||
bool CreateSublayer(const std::string& identifier, int index = -1);
|
||||
bool InsertSublayerPath(const std::string& path, int index = -1);
|
||||
bool RemoveSublayer(int index);
|
||||
bool MoveSublayerUp(int index);
|
||||
bool MoveSublayerDown(int index);
|
||||
|
||||
// Muting
|
||||
void MuteLayer(const std::string& layerIdentifier);
|
||||
void UnmuteLayer(const std::string& layerIdentifier);
|
||||
bool IsLayerMuted(const std::string& layerIdentifier) const;
|
||||
|
||||
// Utility
|
||||
static std::string ExtractDisplayName(const std::string& identifier);
|
||||
|
||||
private:
|
||||
void BuildLayerList();
|
||||
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
std::vector<LayerInfo> m_layers;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "PropertyManager.h"
|
||||
#include "LayerManager.h"
|
||||
#include "../utils/Logger.h"
|
||||
#include <pxr/usd/usd/prim.h>
|
||||
#include <pxr/usd/usd/primRange.h>
|
||||
#include <pxr/usd/usdGeom/tokens.h>
|
||||
#include <pxr/base/gf/vec3f.h>
|
||||
#include <pxr/base/gf/vec3d.h>
|
||||
#include <pxr/base/vt/value.h>
|
||||
|
||||
PXR_NAMESPACE_USING_DIRECTIVE
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
PropertyManager::PropertyManager()
|
||||
: m_stage(nullptr) {
|
||||
}
|
||||
|
||||
PropertyManager::~PropertyManager() {
|
||||
}
|
||||
|
||||
void PropertyManager::SetStage(UsdStageRefPtr stage) {
|
||||
m_stage = stage;
|
||||
m_currentLayer = nullptr;
|
||||
}
|
||||
|
||||
void PropertyManager::SetCurrentLayer(const SdfLayerHandle& layer) {
|
||||
m_currentLayer = layer;
|
||||
}
|
||||
|
||||
std::vector<PropertyInfo> PropertyManager::GetPrimProperties(const std::string& primPath) {
|
||||
UsdPrim prim = GetPrim(primPath);
|
||||
if (!prim.IsValid()) {
|
||||
return {};
|
||||
}
|
||||
return GetPrimProperties(prim);
|
||||
}
|
||||
|
||||
std::vector<PropertyInfo> PropertyManager::GetPrimProperties(const UsdPrim& prim) {
|
||||
std::vector<PropertyInfo> props;
|
||||
if (!prim.IsValid()) return props;
|
||||
|
||||
CollectProperties(prim, props);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
void PropertyManager::CollectProperties(const UsdPrim& prim, std::vector<PropertyInfo>& props) {
|
||||
for (const auto& prop : prim.GetAttributes()) {
|
||||
PropertyInfo info;
|
||||
info.name = prop.GetName().GetString();
|
||||
info.displayName = info.name;
|
||||
info.attribute = prop;
|
||||
info.typeName = prop.GetTypeName().GetAsToken().GetString();
|
||||
info.hasValue = prop.HasValue();
|
||||
info.layerStack = GetPropertyLayerStack(prop);
|
||||
|
||||
if (info.hasValue) {
|
||||
info.value = ExtractValue(prop);
|
||||
}
|
||||
|
||||
props.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyValue PropertyManager::ExtractValue(const UsdAttribute& attr) {
|
||||
PropertyValue result = std::string("");
|
||||
|
||||
auto typeName = attr.GetTypeName();
|
||||
auto roleName = typeName.GetRole();
|
||||
|
||||
if (typeName == SdfValueTypeNames->Bool) {
|
||||
bool val = false;
|
||||
attr.Get(&val);
|
||||
result = val;
|
||||
} else if (typeName == SdfValueTypeNames->Int) {
|
||||
int val = 0;
|
||||
attr.Get(&val);
|
||||
result = val;
|
||||
} else if (typeName == SdfValueTypeNames->Float) {
|
||||
float val = 0.0f;
|
||||
attr.Get(&val);
|
||||
result = val;
|
||||
} else if (typeName == SdfValueTypeNames->Double) {
|
||||
double val = 0.0;
|
||||
attr.Get(&val);
|
||||
result = val;
|
||||
} else if (typeName == SdfValueTypeNames->Float3 || typeName == SdfValueTypeNames->Vector3f) {
|
||||
GfVec3f val(0.0f);
|
||||
attr.Get(&val);
|
||||
result = val;
|
||||
} else if (typeName == SdfValueTypeNames->Double3 || typeName == SdfValueTypeNames->Vector3d) {
|
||||
GfVec3d val(0.0);
|
||||
attr.Get(&val);
|
||||
result = val;
|
||||
} else if (typeName == SdfValueTypeNames->String || typeName == SdfValueTypeNames->Token) {
|
||||
std::string val;
|
||||
attr.Get(&val);
|
||||
result = val;
|
||||
} else {
|
||||
// Fallback: get as string representation
|
||||
VtValue vtVal;
|
||||
if (attr.Get(&vtVal)) {
|
||||
result = vtVal.GetTypeName();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PropertyManager::SetPropertyValue(const std::string& primPath,
|
||||
const std::string& propName,
|
||||
const PropertyValue& value) {
|
||||
UsdPrim prim = GetPrim(primPath);
|
||||
if (!prim.IsValid()) {
|
||||
LOG_ERROR("Invalid prim path: " + primPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
UsdAttribute attr = prim.GetAttribute(TfToken(propName));
|
||||
if (!attr.IsValid()) {
|
||||
LOG_ERROR("Invalid attribute: " + propName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a current layer is set, create an edit context to target it
|
||||
try {
|
||||
if (m_currentLayer) {
|
||||
UsdEditContext editCtx(m_stage, m_currentLayer);
|
||||
return ApplyValue(attr, value);
|
||||
} else {
|
||||
return ApplyValue(attr, value);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Failed to set property: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PropertyManager::SetPropertyValueInLayer(const std::string& primPath,
|
||||
const std::string& propName,
|
||||
const PropertyValue& value,
|
||||
const SdfLayerHandle& layer) {
|
||||
UsdPrim prim = GetPrim(primPath);
|
||||
if (!prim.IsValid()) return false;
|
||||
|
||||
UsdAttribute attr = prim.GetAttribute(TfToken(propName));
|
||||
if (!attr.IsValid()) return false;
|
||||
|
||||
try {
|
||||
UsdEditContext editCtx(m_stage, layer);
|
||||
return ApplyValue(attr, value);
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Failed to set property: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string PropertyManager::GetPropertyLayerStack(const UsdAttribute& attr) {
|
||||
if (!attr.IsValid()) return "";
|
||||
|
||||
SdfPropertySpecHandleVector propStack = attr.GetPropertyStack();
|
||||
std::string result;
|
||||
|
||||
for (size_t i = 0; i < propStack.size(); i++) {
|
||||
if (i > 0) result += " -> ";
|
||||
|
||||
SdfLayerHandle layer = propStack[i]->GetLayer();
|
||||
if (layer) {
|
||||
result += LayerManager::ExtractDisplayName(layer->GetIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> PropertyManager::GetPrimPaths() {
|
||||
std::vector<std::string> paths;
|
||||
if (!m_stage) return paths;
|
||||
|
||||
paths.push_back(m_stage->GetPseudoRoot().GetPath().GetString());
|
||||
|
||||
for (const auto& prim : m_stage->Traverse()) {
|
||||
paths.push_back(prim.GetPath().GetString());
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
UsdPrim PropertyManager::GetPrim(const std::string& path) {
|
||||
if (!m_stage || path.empty()) {
|
||||
return UsdPrim();
|
||||
}
|
||||
|
||||
SdfPath sdfPath(path);
|
||||
return m_stage->GetPrimAtPath(sdfPath);
|
||||
}
|
||||
|
||||
bool PropertyManager::ApplyValue(const UsdAttribute& attr, const PropertyValue& value) {
|
||||
return std::visit([&](auto&& val) -> bool {
|
||||
using T = std::decay_t<decltype(val)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
return attr.Set(val);
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
return attr.Set(val);
|
||||
} else if constexpr (std::is_same_v<T, float>) {
|
||||
return attr.Set(val);
|
||||
} else if constexpr (std::is_same_v<T, double>) {
|
||||
return attr.Set(val);
|
||||
} else if constexpr (std::is_same_v<T, std::string>) {
|
||||
return attr.Set(val);
|
||||
} else if constexpr (std::is_same_v<T, GfVec3f>) {
|
||||
return attr.Set(val);
|
||||
} else if constexpr (std::is_same_v<T, GfVec3d>) {
|
||||
return attr.Set(val);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}, value);
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/usd/attribute.h>
|
||||
#include <pxr/usd/usd/editContext.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
PXR_NAMESPACE_USING_DIRECTIVE
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
using PropertyValue = std::variant<
|
||||
bool, int, float, double, std::string,
|
||||
pxr::GfVec3f, pxr::GfVec3d
|
||||
>;
|
||||
|
||||
struct PropertyInfo {
|
||||
std::string name;
|
||||
std::string displayName;
|
||||
std::string typeName;
|
||||
pxr::UsdAttribute attribute;
|
||||
bool hasValue;
|
||||
PropertyValue value;
|
||||
std::string layerStack;
|
||||
};
|
||||
|
||||
class PropertyManager {
|
||||
public:
|
||||
PropertyManager();
|
||||
~PropertyManager();
|
||||
|
||||
void SetStage(UsdStageRefPtr stage);
|
||||
void SetCurrentLayer(const SdfLayerHandle& layer);
|
||||
SdfLayerHandle GetCurrentLayer() const { return m_currentLayer; }
|
||||
|
||||
// Prim properties
|
||||
std::vector<PropertyInfo> GetPrimProperties(const std::string& primPath);
|
||||
std::vector<PropertyInfo> GetPrimProperties(const UsdPrim& prim);
|
||||
|
||||
// Property editing
|
||||
bool SetPropertyValue(const std::string& primPath, const std::string& propName,
|
||||
const PropertyValue& value);
|
||||
bool SetPropertyValueInLayer(const std::string& primPath, const std::string& propName,
|
||||
const PropertyValue& value, const SdfLayerHandle& layer);
|
||||
|
||||
// Property info
|
||||
std::string GetPropertyLayerStack(const UsdAttribute& attr);
|
||||
|
||||
// Prim hierarchy
|
||||
std::vector<std::string> GetPrimPaths();
|
||||
UsdPrim GetPrim(const std::string& path);
|
||||
|
||||
private:
|
||||
void CollectProperties(const UsdPrim& prim, std::vector<PropertyInfo>& props);
|
||||
PropertyValue ExtractValue(const UsdAttribute& attr);
|
||||
bool ApplyValue(const UsdAttribute& attr, const PropertyValue& value);
|
||||
|
||||
UsdStageRefPtr m_stage;
|
||||
SdfLayerHandle m_currentLayer;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,307 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/imaging/glf/drawTarget.h>
|
||||
#include <pxr/usdImaging/usdImagingGL/engine.h>
|
||||
#include <pxr/imaging/glf/simpleLight.h>
|
||||
#include <pxr/imaging/glf/simpleMaterial.h>
|
||||
#include <pxr/base/gf/vec3f.h>
|
||||
#include <pxr/base/gf/vec4f.h>
|
||||
#include <pxr/base/gf/vec4d.h>
|
||||
#include <pxr/base/gf/matrix4d.h>
|
||||
#include <pxr/base/gf/matrix4f.h>
|
||||
#include <pxr/base/gf/range3d.h>
|
||||
#include <pxr/base/gf/camera.h>
|
||||
#include <pxr/base/gf/bbox3d.h>
|
||||
#include <pxr/base/gf/frustum.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
/// Bounding box display mode for selected prims.
|
||||
enum class BBoxMode {
|
||||
None, ///< No bounding boxes drawn
|
||||
PerObject, ///< One box per selected prim
|
||||
AllSelection ///< One combined box for the entire selection
|
||||
};
|
||||
|
||||
class UsdSceneRenderer {
|
||||
public:
|
||||
UsdSceneRenderer();
|
||||
~UsdSceneRenderer();
|
||||
|
||||
void SetStage(pxr::UsdStageRefPtr stage);
|
||||
|
||||
/// Render the current stage into the internal draw target.
|
||||
void Render(int width, int height);
|
||||
|
||||
/// Compute the world-space bounding box of the whole stage.
|
||||
pxr::GfRange3d ComputeStageBounds();
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Camera state
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Set free-camera view/proj matrices explicitly.
|
||||
void SetCameraState(const pxr::GfMatrix4d& viewMatrix,
|
||||
const pxr::GfMatrix4d& projMatrix);
|
||||
|
||||
/// Set free-camera state from a GfCamera (also extracts clip planes).
|
||||
void SetCameraStateFromGfCamera(const pxr::GfCamera& gfCamera);
|
||||
|
||||
/// Set USD prim camera path; renderer resolves it internally.
|
||||
void SetCameraPath(const pxr::SdfPath& cameraPath);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Selection
|
||||
// -----------------------------------------------------------------------
|
||||
void ClearSelected();
|
||||
void AddSelected(const pxr::SdfPath& path, int instanceIndex = -1);
|
||||
/// Replace the entire selection with a set of paths (for multi-select).
|
||||
void SetSelectedPaths(const pxr::SdfPathVector& paths);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Picking
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Single-prim pick: returns true if a hit was found.
|
||||
bool PickObject(
|
||||
int mouseX, int mouseY,
|
||||
int viewWidth, int viewHeight,
|
||||
pxr::GfVec3d* outHitPoint,
|
||||
pxr::SdfPath* outHitPrimPath);
|
||||
|
||||
/// Rect pick: finds all unique prims whose geometry overlaps the screen rect
|
||||
/// defined by (x0,y0)-(x1,y1) in viewport pixel coordinates.
|
||||
/// Returns true if at least one prim was hit.
|
||||
bool PickObjectsInRect(
|
||||
int x0, int y0, int x1, int y1,
|
||||
int viewWidth, int viewHeight,
|
||||
pxr::SdfPathVector* outHitPaths);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Overlay drawing (called while draw-target FBO is still bound)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Draw XYZ axis gizmo matching stageView.DrawAxis().
|
||||
void DrawAxis(const pxr::GfMatrix4d& viewProjMatrix, double cameraDist);
|
||||
|
||||
/// Draw bounding boxes for the given selected prim paths according to m_bboxMode.
|
||||
void DrawBoundingBoxes(
|
||||
const pxr::SdfPathVector& selectedPaths,
|
||||
const pxr::GfMatrix4d& viewProjMatrix);
|
||||
|
||||
/// Draw frustum wireframes for all UsdGeomCamera prims in the stage.
|
||||
/// selectedPaths — current primary selection (selected camera gets accent colour).
|
||||
/// activeCameraPath — camera currently driving the viewport (gets cyan).
|
||||
/// viewportCameraDist — used to scale wireframe size (from ViewportCamera::GetDist()).
|
||||
void DrawCameraWireframes(
|
||||
pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPathVector& selectedPaths,
|
||||
const pxr::SdfPath& activeCameraPath,
|
||||
const pxr::GfMatrix4d& viewProjMatrix,
|
||||
double viewportCameraDist);
|
||||
|
||||
/// Test a screen-space mouse position against all camera wireframe segments.
|
||||
/// Returns true and sets *outCameraPath if a camera wireframe is within
|
||||
/// kCameraPickRadius (10 px) of mousePos. Prioritises the closest hit.
|
||||
/// mouseX/Y and imagePosX/Y are absolute screen coordinates.
|
||||
bool PickCameraAtPoint(
|
||||
pxr::UsdStageRefPtr stage,
|
||||
float mouseX, float mouseY,
|
||||
const pxr::GfMatrix4d& viewProjMatrix,
|
||||
float imagePosX, float imagePosY,
|
||||
int viewW, int viewH,
|
||||
double viewportCameraDist,
|
||||
pxr::SdfPath* outCameraPath);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Draw-target FBO helpers (for external overlays, e.g. TransformManipulator)
|
||||
// -----------------------------------------------------------------------
|
||||
/// Bind the internal offscreen FBO and set the GL viewport.
|
||||
/// Must be paired with UnbindDrawTarget() after drawing.
|
||||
void BindDrawTarget();
|
||||
/// Unbind the internal offscreen FBO.
|
||||
void UnbindDrawTarget();
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Render delegate
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Return all available renderer plugin IDs (e.g. HdStormRendererPlugin).
|
||||
static std::vector<pxr::TfToken> GetRendererPlugins();
|
||||
|
||||
/// Return the ID of the currently active renderer plugin.
|
||||
pxr::TfToken GetCurrentRendererId() const;
|
||||
|
||||
/// Return the human-readable display name for a given plugin ID.
|
||||
static std::string GetRendererDisplayName(const pxr::TfToken& pluginId);
|
||||
|
||||
/// Switch to a different render delegate. Returns false if the plugin is
|
||||
/// unknown or the switch fails. The renderer is re-initialised if needed.
|
||||
bool SetRendererPlugin(const pxr::TfToken& pluginId);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// View settings
|
||||
// -----------------------------------------------------------------------
|
||||
bool ShowGrid() const { return m_showGrid; }
|
||||
void SetShowGrid(bool show) { m_showGrid = show; }
|
||||
|
||||
bool GetAAEnabled() const { return m_aaEnabled; }
|
||||
void SetAAEnabled(bool enabled) { m_aaEnabled = enabled; }
|
||||
|
||||
const pxr::GfVec3f& GetBackgroundColor() const { return m_backgroundColor; }
|
||||
void SetBackgroundColor(const pxr::GfVec3f& c) { m_backgroundColor = c; }
|
||||
|
||||
void SetForceRefresh(bool val) {
|
||||
m_forceRefresh = m_forceRefresh || val;
|
||||
if (val) m_cameraCacheDirty = true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Bounding box display
|
||||
// -----------------------------------------------------------------------
|
||||
BBoxMode GetBBoxMode() const { return m_bboxMode; }
|
||||
void SetBBoxMode(BBoxMode mode) { m_bboxMode = mode; }
|
||||
|
||||
const pxr::GfVec4f& GetBBoxColor() const { return m_bboxColor; }
|
||||
void SetBBoxColor(const pxr::GfVec4f& c) { m_bboxColor = c; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Lighting settings (mirrors stageView.py viewSettings)
|
||||
// -----------------------------------------------------------------------
|
||||
/// Camera headlight: single point light at the camera position (default ON).
|
||||
bool GetAmbientLightOnly() const { return m_ambientLightOnly; }
|
||||
void SetAmbientLightOnly(bool val) { m_ambientLightOnly = val; }
|
||||
|
||||
/// Dome environment light (default OFF).
|
||||
bool GetDomeLightEnabled() const { return m_domeLightEnabled; }
|
||||
void SetDomeLightEnabled(bool val) { m_domeLightEnabled = val; }
|
||||
|
||||
/// Default material ambient (kA, default 0.2 — matches viewSettingsDataModel.py).
|
||||
float GetDefaultMaterialAmbient() const { return m_defaultMaterialAmbient; }
|
||||
void SetDefaultMaterialAmbient(float v) { m_defaultMaterialAmbient = v; }
|
||||
|
||||
/// Default material specular (kS, default 0.1 — matches viewSettingsDataModel.py).
|
||||
float GetDefaultMaterialSpecular() const { return m_defaultMaterialSpecular; }
|
||||
void SetDefaultMaterialSpecular(float v) { m_defaultMaterialSpecular = v; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Output
|
||||
// -----------------------------------------------------------------------
|
||||
uint32_t GetColorTextureID();
|
||||
|
||||
// For unit tests
|
||||
pxr::GlfDrawTargetRefPtr GetDrawTargetForTest() const { return m_drawTarget; }
|
||||
|
||||
private:
|
||||
void InitRenderer();
|
||||
void InitGridResources();
|
||||
void RebuildGridVBO(); ///< (Re)build grid line geometry after up-axis or size change.
|
||||
void DestroyGridResources();
|
||||
void RenderGrid(int width, int height);
|
||||
void InitAxisResources();
|
||||
void DestroyAxisResources();
|
||||
void InitBBoxResources();
|
||||
void DestroyBBoxResources();
|
||||
void InitCamWireResources();
|
||||
void DestroyCamWireResources();
|
||||
/// Draw a single axis-aligned box from a GfRange3d.
|
||||
void DrawBox(const pxr::GfRange3d& range, const pxr::GfMatrix4f& mvp);
|
||||
|
||||
/// Build camera wireframe line segments (GL_LINES vertex pairs) in world space
|
||||
/// for a single camera given its resolved GfCamera and display scale.
|
||||
/// outVerts is appended with interleaved XYZ floats (2 verts per segment).
|
||||
void BuildCameraWireframeLines(const pxr::GfCamera& gfCam,
|
||||
double scale,
|
||||
std::vector<float>& outVerts);
|
||||
|
||||
/// Project a world-space point to absolute screen coordinates (x=imagePosX+pixelX, etc.).
|
||||
/// Returns false when the point is behind the camera.
|
||||
static bool WorldToScreen(const pxr::GfVec3d& world,
|
||||
const pxr::GfMatrix4d& viewProj,
|
||||
int viewW, int viewH,
|
||||
float imagePosX, float imagePosY,
|
||||
float& outX, float& outY);
|
||||
|
||||
/// 2-D point-to-segment distance (all values in screen pixels).
|
||||
static float PointToSegmentDist(float px, float py,
|
||||
float ax, float ay,
|
||||
float bx, float by);
|
||||
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
std::shared_ptr<pxr::UsdImagingGLEngine> m_renderer;
|
||||
pxr::TfToken m_currentRendererPlugin; ///< Active plugin ID (empty = default)
|
||||
pxr::GlfDrawTargetRefPtr m_drawTarget;
|
||||
|
||||
pxr::UsdImagingGLRenderParams m_renderParams;
|
||||
|
||||
pxr::GfMatrix4d m_viewMatrix;
|
||||
pxr::GfMatrix4d m_projMatrix;
|
||||
pxr::SdfPath m_cameraPath;
|
||||
bool m_useCameraPath;
|
||||
|
||||
// Camera frustum used for picking (set when camera state is provided via GfCamera)
|
||||
pxr::GfFrustum m_cameraFrustum;
|
||||
bool m_hasCameraFrustum = false;
|
||||
|
||||
// Clip planes extracted from GfCamera (passed to renderParams)
|
||||
std::vector<pxr::GfVec4d> m_clipPlanes;
|
||||
|
||||
bool m_showGrid;
|
||||
bool m_aaEnabled; ///< GL_LINE_SMOOTH anti-aliasing for all line overlays
|
||||
pxr::GfVec3f m_backgroundColor;
|
||||
|
||||
// Lighting settings (mirrors stageView.py viewSettings)
|
||||
bool m_ambientLightOnly; // camera headlight
|
||||
bool m_domeLightEnabled; // dome/IBL light
|
||||
bool m_stageIsZup; // used for dome light rotation
|
||||
float m_defaultMaterialAmbient; // kA
|
||||
float m_defaultMaterialSpecular;// kS
|
||||
|
||||
bool m_rendererInitialized;
|
||||
bool m_forceRefresh;
|
||||
|
||||
int m_diagFrameCount;
|
||||
|
||||
// --- Grid GL resources (line geometry, reuses m_axisProgram / m_axisUniformMVP) ---
|
||||
GLuint m_gridVAO;
|
||||
GLuint m_gridVBO;
|
||||
// Draw-call ranges inside the VBO (vertex index, count pairs for GL_LINES)
|
||||
GLint m_gridMinorFirst, m_gridMinorCount; ///< 1-unit minor lines
|
||||
GLint m_gridMajorFirst, m_gridMajorCount; ///< 10-unit major lines
|
||||
GLint m_gridAxisAFirst, m_gridAxisACount; ///< X-axis (red, at b=0)
|
||||
GLint m_gridAxisBFirst, m_gridAxisBCount; ///< Z/Y-axis(blue/green, at a=0)
|
||||
float m_gridHalfSize; ///< Half-extent (default 50 → 100×100 grid)
|
||||
|
||||
// --- Axis GLSL resources (stageView.DrawAxis port) ---
|
||||
GLuint m_axisVAO;
|
||||
GLuint m_axisVBO;
|
||||
GLuint m_axisProgram;
|
||||
GLint m_axisUniformMVP;
|
||||
GLint m_axisUniformColor;
|
||||
|
||||
// --- Bounding box GLSL resources ---
|
||||
BBoxMode m_bboxMode;
|
||||
pxr::GfVec4f m_bboxColor; // default: white
|
||||
GLuint m_bboxVAO;
|
||||
GLuint m_bboxVBO; // dynamic: updated per box
|
||||
GLuint m_bboxProgram;
|
||||
GLint m_bboxUniformMVP;
|
||||
GLint m_bboxUniformColor;
|
||||
|
||||
// --- Camera wireframe GL resources (dedicated, separate from bbox) ---
|
||||
GLuint m_camWireVAO = 0;
|
||||
GLuint m_camWireVBO = 0; ///< dynamic VBO; reallocated per frame as needed
|
||||
|
||||
// --- Camera wireframe cache ---
|
||||
pxr::SdfPathVector m_cachedCameraPaths;
|
||||
bool m_cameraCacheDirty = true;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,248 @@
|
||||
#include "UsdStageManager.h"
|
||||
#include "../utils/Logger.h"
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <pxr/usd/sdf/primSpec.h>
|
||||
#include <pxr/usd/sdf/copyUtils.h>
|
||||
|
||||
PXR_NAMESPACE_USING_DIRECTIVE
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
/// Merge all root-prim specs from the session layer into the root layer so
|
||||
/// they are preserved when the stage is saved. The session layer is cleared
|
||||
/// afterwards so that the opinions are not double-applied when the file is
|
||||
/// reopened. No-op if the session layer is empty.
|
||||
static void MergeSessionLayerIntoRoot(UsdStageRefPtr stage) {
|
||||
if (!stage) return;
|
||||
|
||||
SdfLayerHandle sessionLayer = stage->GetSessionLayer();
|
||||
SdfLayerHandle rootLayer = stage->GetRootLayer();
|
||||
if (!sessionLayer || !rootLayer) return;
|
||||
if (sessionLayer->IsEmpty()) return;
|
||||
|
||||
LOG_INFO("Merging session layer into root layer before save");
|
||||
|
||||
// Iterate the immediate children of the pseudo-root in the session layer
|
||||
// (these are all root-level prims that have opinions there).
|
||||
SdfPrimSpecHandle pseudoRoot = sessionLayer->GetPseudoRoot();
|
||||
if (!pseudoRoot) return;
|
||||
|
||||
for (const SdfPrimSpecHandle& primSpec : pseudoRoot->GetNameChildren()) {
|
||||
if (!primSpec) continue;
|
||||
const SdfPath& primPath = primSpec->GetPath();
|
||||
if (!SdfCopySpec(sessionLayer, primPath, rootLayer, primPath)) {
|
||||
LOG_ERROR("MergeSessionLayer: SdfCopySpec failed for " + primPath.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
sessionLayer->Clear();
|
||||
LOG_INFO("Session layer merged and cleared");
|
||||
}
|
||||
|
||||
UsdStageManager::UsdStageManager()
|
||||
: m_stage(nullptr) {
|
||||
}
|
||||
|
||||
UsdStageManager::~UsdStageManager() {
|
||||
CloseStage();
|
||||
}
|
||||
|
||||
bool UsdStageManager::OpenStage(const std::string& filePath) {
|
||||
LOG_INFO("Opening USD stage: " + filePath);
|
||||
|
||||
try {
|
||||
// Close existing stage if any
|
||||
CloseStage();
|
||||
|
||||
// Open the stage
|
||||
m_stage = UsdStage::Open(filePath);
|
||||
|
||||
if (!m_stage) {
|
||||
SetError("Failed to open USD stage: " + filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Successfully opened USD stage: " + filePath);
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
SetError(std::string("Exception while opening stage: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UsdStageManager::CreateNewStage(const std::string& filePath) {
|
||||
LOG_INFO("Creating new USD stage: " + filePath);
|
||||
|
||||
try {
|
||||
// Close existing stage if any
|
||||
CloseStage();
|
||||
|
||||
// Create new stage
|
||||
m_stage = UsdStage::CreateNew(filePath);
|
||||
|
||||
if (!m_stage) {
|
||||
SetError("Failed to create new USD stage: " + filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Successfully created new USD stage: " + filePath);
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
SetError(std::string("Exception while creating stage: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UsdStageManager::CreateInMemoryStage() {
|
||||
LOG_INFO("Creating in-memory USD stage");
|
||||
|
||||
try {
|
||||
// Close existing stage if any
|
||||
CloseStage();
|
||||
|
||||
// Create in-memory stage
|
||||
m_stage = UsdStage::CreateInMemory();
|
||||
|
||||
if (!m_stage) {
|
||||
SetError("Failed to create in-memory USD stage");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Successfully created in-memory USD stage");
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
SetError(std::string("Exception while creating in-memory stage: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void UsdStageManager::CloseStage() {
|
||||
if (m_stage) {
|
||||
LOG_INFO("Closing USD stage");
|
||||
m_stage.Reset();
|
||||
m_stage = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool UsdStageManager::SaveStage() {
|
||||
if (!m_stage) {
|
||||
SetError("No stage to save");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
LOG_INFO("Saving USD stage");
|
||||
|
||||
// Persist any session-layer opinions into the root layer so they are
|
||||
// not lost when the file is saved (the session layer is anonymous and
|
||||
// cannot be saved by UsdStage::Save() itself).
|
||||
MergeSessionLayerIntoRoot(m_stage);
|
||||
|
||||
m_stage->Save();
|
||||
LOG_INFO("Successfully saved USD stage");
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
SetError(std::string("Exception while saving stage: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UsdStageManager::SaveStageAs(const std::string& filePath) {
|
||||
if (!m_stage) {
|
||||
SetError("No stage to save");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
LOG_INFO("Saving USD stage as: " + filePath);
|
||||
|
||||
// Merge session-layer opinions into the root layer so they survive
|
||||
// the copy. The session layer is anonymous and cannot be exported
|
||||
// directly; its content must live in the root layer to be persisted.
|
||||
MergeSessionLayerIntoRoot(m_stage);
|
||||
|
||||
// Export the root layer to the new path. Unlike UsdStage::Export()
|
||||
// (which flattens all sublayers into one file), SdfLayer::Export()
|
||||
// writes only the root layer's own opinions while preserving its
|
||||
// subLayerPaths references. This keeps the sublayer structure intact.
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) {
|
||||
SetError("No root layer to export");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rootLayer->Export(filePath)) {
|
||||
SetError("Failed to export root layer to: " + filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reopen the stage from the newly written file so the application
|
||||
// reflects the saved path from this point on.
|
||||
m_stage = UsdStage::Open(filePath);
|
||||
if (!m_stage) {
|
||||
SetError("Failed to reopen stage after save as: " + filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("Successfully saved USD stage as: " + filePath);
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
SetError(std::string("Exception while saving stage as: ") + e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string UsdStageManager::GetRootLayerPath() const {
|
||||
if (!m_stage) {
|
||||
return "";
|
||||
}
|
||||
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return rootLayer->GetRealPath();
|
||||
}
|
||||
|
||||
std::string UsdStageManager::GetRootLayerIdentifier() const {
|
||||
if (!m_stage) {
|
||||
return "";
|
||||
}
|
||||
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return rootLayer->GetIdentifier();
|
||||
}
|
||||
|
||||
SdfLayerHandle UsdStageManager::GetRootLayer() const {
|
||||
if (!m_stage) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_stage->GetRootLayer();
|
||||
}
|
||||
|
||||
SdfLayerHandle UsdStageManager::GetSessionLayer() const {
|
||||
if (!m_stage) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_stage->GetSessionLayer();
|
||||
}
|
||||
|
||||
void UsdStageManager::SetError(const std::string& error) {
|
||||
m_lastError = error;
|
||||
LOG_ERROR(error);
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
class UsdStageManager {
|
||||
public:
|
||||
UsdStageManager();
|
||||
~UsdStageManager();
|
||||
|
||||
// Stage lifecycle management
|
||||
bool OpenStage(const std::string& filePath);
|
||||
bool CreateNewStage(const std::string& filePath);
|
||||
bool CreateInMemoryStage();
|
||||
void CloseStage();
|
||||
|
||||
// Stage operations
|
||||
bool SaveStage();
|
||||
bool SaveStageAs(const std::string& filePath);
|
||||
|
||||
// Stage access
|
||||
pxr::UsdStageRefPtr GetStage() const { return m_stage; }
|
||||
bool HasStage() const { return m_stage != nullptr; }
|
||||
|
||||
// Stage information
|
||||
std::string GetRootLayerPath() const;
|
||||
std::string GetRootLayerIdentifier() const;
|
||||
pxr::SdfLayerHandle GetRootLayer() const;
|
||||
pxr::SdfLayerHandle GetSessionLayer() const;
|
||||
|
||||
// Error handling
|
||||
std::string GetLastError() const { return m_lastError; }
|
||||
|
||||
private:
|
||||
void SetError(const std::string& error);
|
||||
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
std::string m_lastError;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,384 @@
|
||||
#include "ViewportCamera.h"
|
||||
|
||||
#include <pxr/usd/usdGeom/metrics.h>
|
||||
#include <pxr/usd/usdGeom/tokens.h>
|
||||
#include <pxr/usd/usdGeom/camera.h>
|
||||
#include <pxr/base/gf/rotation.h>
|
||||
#include <pxr/base/gf/frustum.h>
|
||||
#include <pxr/imaging/cameraUtil/conformWindow.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: rotation matrix around `axis` by `angleDeg` degrees
|
||||
// ---------------------------------------------------------------------------
|
||||
static pxr::GfMatrix4d RotMatrix(const pxr::GfVec3d& axis, double angleDeg)
|
||||
{
|
||||
return pxr::GfMatrix4d(1.0).SetRotate(pxr::GfRotation(axis, angleDeg));
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Construction
|
||||
// ===========================================================================
|
||||
|
||||
ViewportCamera::ViewportCamera()
|
||||
: m_cameraTransformDirty(true)
|
||||
, m_rotTheta(0.0)
|
||||
, m_rotPhi(0.0)
|
||||
, m_rotPsi(0.0)
|
||||
, m_center(0.0, 0.0, 0.0)
|
||||
, m_dist(100.0)
|
||||
, m_selSize(10.0)
|
||||
, m_isZUp(false)
|
||||
, m_YZUpMatrix(1.0)
|
||||
, m_YZUpInvMatrix(1.0)
|
||||
, m_hasClosestVisibleDist(false)
|
||||
, m_closestVisibleDist(0.0)
|
||||
, m_lastFramedDist(100.0)
|
||||
, m_lastFramedClosestDist(0.0)
|
||||
, m_overrideNear(-1.0)
|
||||
, m_overrideFar(-1.0)
|
||||
, m_mode(CameraMode::Free)
|
||||
{
|
||||
// Default: perspective camera, vertical FOV = 60°, square aspect ratio.
|
||||
m_camera.SetPerspectiveFromAspectRatioAndFieldOfView(
|
||||
1.0f, 60.0f, pxr::GfCamera::FOVVertical);
|
||||
m_camera.SetFocusDistance(static_cast<float>(m_dist));
|
||||
ResetClippingPlanes();
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Stage
|
||||
// ===========================================================================
|
||||
|
||||
void ViewportCamera::SetStage(pxr::UsdStageRefPtr stage)
|
||||
{
|
||||
m_stage = stage;
|
||||
m_isZUp = stage &&
|
||||
(pxr::UsdGeomGetStageUpAxis(stage) == pxr::UsdGeomTokens->z);
|
||||
|
||||
if (m_isZUp) {
|
||||
// GfCamera.Y_UP_TO_Z_UP_MATRIX: rotate -90° around X axis
|
||||
m_YZUpMatrix = pxr::GfMatrix4d(1.0).SetRotate(
|
||||
pxr::GfRotation(pxr::GfVec3d::XAxis(), -90.0));
|
||||
m_YZUpInvMatrix = m_YZUpMatrix.GetInverse();
|
||||
} else {
|
||||
m_YZUpMatrix = pxr::GfMatrix4d(1.0);
|
||||
m_YZUpInvMatrix = pxr::GfMatrix4d(1.0);
|
||||
}
|
||||
m_cameraTransformDirty = true;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Private: rebuild camera transform from orbital parameters
|
||||
// Mirrors FreeCamera._pushToCameraTransform()
|
||||
// ===========================================================================
|
||||
|
||||
void ViewportCamera::PushToCameraTransform()
|
||||
{
|
||||
if (!m_cameraTransformDirty) return;
|
||||
|
||||
// camera-to-world transform (same as FreeCamera._pushToCameraTransform):
|
||||
// T(dist*Z) * R(-psi,Z) * R(-phi,X) * R(-theta,Y) * YZUpInv * T(center)
|
||||
pxr::GfMatrix4d xform =
|
||||
pxr::GfMatrix4d(1.0).SetTranslate(pxr::GfVec3d::ZAxis() * m_dist)
|
||||
* RotMatrix(pxr::GfVec3d::ZAxis(), -m_rotPsi)
|
||||
* RotMatrix(pxr::GfVec3d::XAxis(), -m_rotPhi)
|
||||
* RotMatrix(pxr::GfVec3d::YAxis(), -m_rotTheta)
|
||||
* m_YZUpInvMatrix
|
||||
* pxr::GfMatrix4d(1.0).SetTranslate(m_center);
|
||||
|
||||
m_camera.SetTransform(xform);
|
||||
m_camera.SetFocusDistance(static_cast<float>(m_dist));
|
||||
m_cameraTransformDirty = false;
|
||||
}
|
||||
|
||||
// Mirrors FreeCamera._pullFromCameraTransform()
|
||||
void ViewportCamera::PullFromCameraTransform()
|
||||
{
|
||||
pxr::GfFrustum frustum = m_camera.GetFrustum();
|
||||
m_dist = static_cast<double>(m_camera.GetFocusDistance());
|
||||
m_selSize = m_dist / 10.0;
|
||||
m_center = frustum.GetPosition() + m_dist * frustum.ComputeViewDirection();
|
||||
|
||||
pxr::GfMatrix4d camTransform = m_camera.GetTransform() * m_YZUpMatrix;
|
||||
camTransform.Orthonormalize();
|
||||
pxr::GfRotation rotation = camTransform.ExtractRotation();
|
||||
|
||||
// Decompose: Y → theta, X → phi, Z → psi
|
||||
pxr::GfVec3d angles = rotation.Decompose(
|
||||
pxr::GfVec3d::YAxis(),
|
||||
pxr::GfVec3d::XAxis(),
|
||||
pxr::GfVec3d::ZAxis());
|
||||
|
||||
m_rotTheta = -angles[0];
|
||||
m_rotPhi = -angles[1];
|
||||
m_rotPsi = -angles[2];
|
||||
m_cameraTransformDirty = true;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Free Camera Operations
|
||||
// ===========================================================================
|
||||
|
||||
void ViewportCamera::Tumble(double dTheta, double dPhi)
|
||||
{
|
||||
m_rotTheta += dTheta;
|
||||
m_rotPhi += dPhi;
|
||||
m_cameraTransformDirty = true;
|
||||
}
|
||||
|
||||
void ViewportCamera::AdjustDistance(double scaleFactor)
|
||||
{
|
||||
// Mirrors FreeCamera.AdjustDistance(): prevents getting stuck near dist≈0.
|
||||
if (scaleFactor > 1.0 && m_dist < 2.0) {
|
||||
double selBasedIncr = m_selSize / 25.0;
|
||||
scaleFactor -= 1.0;
|
||||
m_dist += std::min(selBasedIncr, scaleFactor);
|
||||
} else {
|
||||
m_dist *= scaleFactor;
|
||||
}
|
||||
m_dist = std::max(m_dist, 0.001); // never let dist reach zero
|
||||
|
||||
// Keep closest-visible-distance estimate in sync with new dist
|
||||
if (m_hasClosestVisibleDist) {
|
||||
if (m_dist > m_lastFramedDist) {
|
||||
m_closestVisibleDist = m_lastFramedClosestDist;
|
||||
} else {
|
||||
m_closestVisibleDist = m_lastFramedClosestDist
|
||||
- m_lastFramedDist
|
||||
+ m_dist;
|
||||
}
|
||||
}
|
||||
m_cameraTransformDirty = true;
|
||||
}
|
||||
|
||||
void ViewportCamera::Truck(double deltaRight, double deltaUp)
|
||||
{
|
||||
PushToCameraTransform();
|
||||
pxr::GfFrustum frustum = m_camera.GetFrustum();
|
||||
pxr::GfVec3d camUp = frustum.ComputeUpVector();
|
||||
pxr::GfVec3d camRight = pxr::GfCross(frustum.ComputeViewDirection(), camUp);
|
||||
m_center += deltaRight * camRight + deltaUp * camUp;
|
||||
m_cameraTransformDirty = true;
|
||||
}
|
||||
|
||||
double ViewportCamera::ComputePixelsToWorldFactor(double viewportHeight)
|
||||
{
|
||||
PushToCameraTransform();
|
||||
pxr::GfFrustum frustum = m_camera.GetFrustum();
|
||||
double frustumHeight = frustum.GetWindow().GetSize()[1];
|
||||
return frustumHeight * m_dist / std::max(viewportHeight, 1.0);
|
||||
}
|
||||
|
||||
void ViewportCamera::FrameSelection(const pxr::GfBBox3d& selBBox, double frameFit)
|
||||
{
|
||||
m_hasClosestVisibleDist = false;
|
||||
m_center = selBBox.ComputeCentroid();
|
||||
|
||||
pxr::GfRange3d selRange = selBBox.ComputeAlignedRange();
|
||||
pxr::GfVec3d sz = selRange.GetSize();
|
||||
m_selSize = std::max({ sz[0], sz[1], sz[2] });
|
||||
|
||||
// Distance calculation from FreeCamera.frameSelection()
|
||||
double fovRad = GetFOV() * M_PI / 180.0;
|
||||
double halfFovRad = std::max(fovRad * 0.5, 0.00872665); // at least ~0.5°
|
||||
double lengthToFit = m_selSize * frameFit * 0.5;
|
||||
m_dist = lengthToFit / std::tan(halfFovRad);
|
||||
|
||||
// Prevent camera from intersecting the bounding box
|
||||
if (m_dist < kDefaultNear + m_selSize * 0.5) {
|
||||
m_dist = kDefaultNear + lengthToFit;
|
||||
}
|
||||
m_cameraTransformDirty = true;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Clipping Planes
|
||||
// ===========================================================================
|
||||
|
||||
std::pair<double,double> ViewportCamera::RangeOfBoxAlongRay(
|
||||
const pxr::GfRay& camRay, const pxr::GfBBox3d& bbox) const
|
||||
{
|
||||
double maxDist = -1e38;
|
||||
double minDist = 1e38;
|
||||
const pxr::GfRange3d& boxRange = bbox.GetRange();
|
||||
const pxr::GfMatrix4d& boxXform = bbox.GetMatrix();
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
pxr::GfVec3d corner = boxXform.Transform(boxRange.GetCorner(i));
|
||||
double t = 0.0;
|
||||
camRay.FindClosestPoint(corner, &t);
|
||||
maxDist = std::max(maxDist, t);
|
||||
minDist = std::min(minDist, t);
|
||||
}
|
||||
minDist = (minDist < kDefaultNear) ? kDefaultNear : minDist * 0.99;
|
||||
maxDist *= 1.01;
|
||||
return { minDist, maxDist };
|
||||
}
|
||||
|
||||
void ViewportCamera::SetClippingPlanes(const pxr::GfBBox3d& stageBBox)
|
||||
{
|
||||
double computedNear, computedFar;
|
||||
|
||||
if (stageBBox.GetRange().IsEmpty()) {
|
||||
computedNear = kDefaultNear;
|
||||
computedFar = kDefaultFar;
|
||||
} else {
|
||||
pxr::GfFrustum frustum = m_camera.GetFrustum();
|
||||
pxr::GfVec3d camPos = frustum.GetPosition();
|
||||
pxr::GfRay camRay(camPos, frustum.ComputeViewDirection());
|
||||
|
||||
auto boxRange = RangeOfBoxAlongRay(camRay, stageBBox);
|
||||
computedNear = boxRange.first;
|
||||
computedFar = boxRange.second;
|
||||
|
||||
double precisionNear = computedFar / kMaxGoodZResolution;
|
||||
if (m_hasClosestVisibleDist) {
|
||||
double halfClose = m_closestVisibleDist / 2.0;
|
||||
if (m_closestVisibleDist < m_lastFramedClosestDist) {
|
||||
halfClose = std::max({ precisionNear, halfClose, computedNear });
|
||||
}
|
||||
if (halfClose < computedNear) {
|
||||
computedNear = halfClose;
|
||||
} else if (precisionNear > computedNear) {
|
||||
computedNear = std::min((precisionNear + halfClose) / 2.0, halfClose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double nearVal = (m_overrideNear > 0.0) ? m_overrideNear : computedNear;
|
||||
double farVal = (m_overrideFar > 0.0) ? m_overrideFar : computedFar;
|
||||
farVal = std::max(nearVal + 1.0, farVal);
|
||||
m_camera.SetClippingRange(pxr::GfRange1f(
|
||||
static_cast<float>(nearVal), static_cast<float>(farVal)));
|
||||
}
|
||||
|
||||
void ViewportCamera::ResetClippingPlanes()
|
||||
{
|
||||
double nearVal = (m_overrideNear > 0.0) ? m_overrideNear : kDefaultNear;
|
||||
double farVal = (m_overrideFar > 0.0) ? m_overrideFar : kDefaultFar;
|
||||
m_camera.SetClippingRange(pxr::GfRange1f(
|
||||
static_cast<float>(nearVal), static_cast<float>(farVal)));
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Camera Resolution
|
||||
// ===========================================================================
|
||||
|
||||
pxr::GfCamera ViewportCamera::ComputeGfCamera(
|
||||
const pxr::GfBBox3d& stageBBox, bool autoClip)
|
||||
{
|
||||
PushToCameraTransform();
|
||||
if (autoClip) {
|
||||
SetClippingPlanes(stageBBox);
|
||||
} else {
|
||||
ResetClippingPlanes();
|
||||
}
|
||||
return m_camera;
|
||||
}
|
||||
|
||||
void ViewportCamera::SetClosestVisibleDistFromPoint(const pxr::GfVec3d& point)
|
||||
{
|
||||
PushToCameraTransform();
|
||||
pxr::GfFrustum frustum = m_camera.GetFrustum();
|
||||
pxr::GfVec3d camPos = frustum.GetPosition();
|
||||
pxr::GfRay camRay(camPos, frustum.ComputeViewDirection());
|
||||
double t = 0.0;
|
||||
camRay.FindClosestPoint(point, &t);
|
||||
m_closestVisibleDist = t;
|
||||
m_hasClosestVisibleDist = true;
|
||||
m_lastFramedDist = m_dist;
|
||||
m_lastFramedClosestDist = m_closestVisibleDist;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Matrix Accessors
|
||||
// ===========================================================================
|
||||
|
||||
pxr::GfMatrix4d ViewportCamera::GetViewMatrix()
|
||||
{
|
||||
PushToCameraTransform();
|
||||
return m_camera.GetFrustum().ComputeViewMatrix();
|
||||
}
|
||||
|
||||
pxr::GfMatrix4d ViewportCamera::GetProjectionMatrix()
|
||||
{
|
||||
PushToCameraTransform();
|
||||
return m_camera.GetFrustum().ComputeProjectionMatrix();
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Compatibility Accessors
|
||||
// ===========================================================================
|
||||
|
||||
pxr::GfVec3d ViewportCamera::GetEye()
|
||||
{
|
||||
PushToCameraTransform();
|
||||
return m_camera.GetFrustum().GetPosition();
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Camera Mode
|
||||
// ===========================================================================
|
||||
|
||||
void ViewportCamera::SetUsdCamera(const pxr::SdfPath& cameraPath)
|
||||
{
|
||||
m_mode = CameraMode::UsdCamera;
|
||||
m_usdCameraPath = cameraPath;
|
||||
}
|
||||
|
||||
void ViewportCamera::SwitchToFreeCamera(const pxr::GfCamera* lastGfCamera)
|
||||
{
|
||||
if (m_mode == CameraMode::Free) return;
|
||||
|
||||
if (lastGfCamera) {
|
||||
// Initialize free-camera state from the last rendered GfCamera
|
||||
// (mirrors FreeCamera.FromGfCamera)
|
||||
m_camera = *lastGfCamera;
|
||||
PullFromCameraTransform();
|
||||
}
|
||||
m_mode = CameraMode::Free;
|
||||
m_usdCameraPath = pxr::SdfPath();
|
||||
m_cameraTransformDirty = true;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Camera Settings
|
||||
// ===========================================================================
|
||||
|
||||
double ViewportCamera::GetFOV() const
|
||||
{
|
||||
return static_cast<double>(
|
||||
m_camera.GetFieldOfView(pxr::GfCamera::FOVVertical));
|
||||
}
|
||||
|
||||
void ViewportCamera::SetFOV(double fov)
|
||||
{
|
||||
m_camera.SetPerspectiveFromAspectRatioAndFieldOfView(
|
||||
m_camera.GetAspectRatio(),
|
||||
static_cast<float>(fov),
|
||||
pxr::GfCamera::FOVVertical);
|
||||
}
|
||||
|
||||
double ViewportCamera::GetAspectRatio() const
|
||||
{
|
||||
return static_cast<double>(m_camera.GetAspectRatio());
|
||||
}
|
||||
|
||||
void ViewportCamera::SetAspectRatio(double aspect)
|
||||
{
|
||||
m_camera.SetPerspectiveFromAspectRatioAndFieldOfView(
|
||||
static_cast<float>(aspect),
|
||||
m_camera.GetFieldOfView(pxr::GfCamera::FOVVertical),
|
||||
pxr::GfCamera::FOVVertical);
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
|
||||
#include <pxr/base/gf/camera.h>
|
||||
#include <pxr/base/gf/matrix4d.h>
|
||||
#include <pxr/base/gf/vec3d.h>
|
||||
#include <pxr/base/gf/bbox3d.h>
|
||||
#include <pxr/base/gf/range3d.h>
|
||||
#include <pxr/base/gf/ray.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <utility>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
/// Free camera ported from Pixar's usdview FreeCamera (freeCamera.py).
|
||||
/// Supports Tumble/Truck/AdjustDistance with Z-up stage handling,
|
||||
/// automatic near/far clipping plane computation from scene bbox,
|
||||
/// and USD prim-camera passthrough mode.
|
||||
class ViewportCamera {
|
||||
public:
|
||||
enum class CameraMode { Free, UsdCamera };
|
||||
|
||||
static constexpr double kDefaultNear = 1.0;
|
||||
static constexpr double kDefaultFar = 2000000.0;
|
||||
static constexpr double kMaxSafeZResolution = 1e6;
|
||||
static constexpr double kMaxGoodZResolution = 5e4;
|
||||
|
||||
ViewportCamera();
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Stage (sets Z-up flag and YZUp matrices)
|
||||
// -----------------------------------------------------------------------
|
||||
void SetStage(pxr::UsdStageRefPtr stage);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Free Camera Operations (mirrors FreeCamera.py)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Orbit (tumble) around center by dTheta (horizontal) and dPhi (vertical) degrees.
|
||||
void Tumble(double dTheta, double dPhi);
|
||||
|
||||
/// Scale distance from center by scaleFactor. Prevents getting stuck near zero.
|
||||
void AdjustDistance(double scaleFactor);
|
||||
|
||||
/// Pan (truck) in camera-local right/up directions, world-unit deltas.
|
||||
void Truck(double deltaRight, double deltaUp);
|
||||
|
||||
/// Returns pixels-to-world factor for correct Truck() scaling.
|
||||
double ComputePixelsToWorldFactor(double viewportHeight);
|
||||
|
||||
/// Frame a bounding box. frameFit=1.1 gives ~10% margin (usdview default).
|
||||
void FrameSelection(const pxr::GfBBox3d& selBBox, double frameFit = 1.1);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Camera Resolution: returns GfCamera with updated transform + clipping
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Returns the GfCamera with up-to-date transform.
|
||||
/// If autoClip=true, near/far are computed from stageBBox for best precision.
|
||||
pxr::GfCamera ComputeGfCamera(const pxr::GfBBox3d& stageBBox,
|
||||
bool autoClip = false);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Convenience Matrix Accessors
|
||||
// -----------------------------------------------------------------------
|
||||
pxr::GfMatrix4d GetViewMatrix();
|
||||
pxr::GfMatrix4d GetProjectionMatrix();
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Camera Mode
|
||||
// -----------------------------------------------------------------------
|
||||
CameraMode GetMode() const { return m_mode; }
|
||||
const pxr::SdfPath& GetUsdCameraPath() const { return m_usdCameraPath; }
|
||||
|
||||
/// Switch to USD prim camera mode (renderer will call SetCameraPath).
|
||||
void SetUsdCamera(const pxr::SdfPath& cameraPath);
|
||||
|
||||
/// Switch back to free camera, optionally initializing state from lastGfCamera.
|
||||
void SwitchToFreeCamera(const pxr::GfCamera* lastGfCamera = nullptr);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Camera Settings
|
||||
// -----------------------------------------------------------------------
|
||||
double GetFOV() const; ///< Vertical FOV in degrees
|
||||
void SetFOV(double fov);
|
||||
double GetAspectRatio() const;
|
||||
void SetAspectRatio(double aspect);
|
||||
|
||||
/// Hint for autoClip: the closest visible geometry point from pick result.
|
||||
void SetClosestVisibleDistFromPoint(const pxr::GfVec3d& point);
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// State Queries
|
||||
// -----------------------------------------------------------------------
|
||||
double GetDist() const { return m_dist; }
|
||||
bool IsZUp() const { return m_isZUp; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Compatibility accessors (for tests and legacy callers)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Returns camera position in world space.
|
||||
pxr::GfVec3d GetEye();
|
||||
|
||||
/// Returns the look-at focal point (center of orbit).
|
||||
const pxr::GfVec3d& GetFocalPoint() const { return m_center; }
|
||||
|
||||
/// Set the look-at focal point directly.
|
||||
/// Used by orthographic view panning to move the center in correct
|
||||
/// view-space directions without going through the perspective Truck() path.
|
||||
void SetFocalPoint(const pxr::GfVec3d& center) {
|
||||
m_center = center;
|
||||
m_cameraTransformDirty = true;
|
||||
}
|
||||
|
||||
/// Returns the current near-clip distance.
|
||||
double GetNearClip() const {
|
||||
return static_cast<double>(m_camera.GetClippingRange().GetMin());
|
||||
}
|
||||
|
||||
/// Returns the current far-clip distance.
|
||||
double GetFarClip() const {
|
||||
return static_cast<double>(m_camera.GetClippingRange().GetMax());
|
||||
}
|
||||
|
||||
/// Legacy: frame a GfRange3d (wraps it in a unit-matrix GfBBox3d).
|
||||
void FrameBoundingBox(const pxr::GfRange3d& range, double frameFit = 1.1) {
|
||||
FrameSelection(pxr::GfBBox3d(range), frameFit);
|
||||
}
|
||||
|
||||
/// Legacy: orbit alias (matches old Orbit(deltaYaw, deltaPitch) signature).
|
||||
void Orbit(double deltaYaw, double deltaPitch) {
|
||||
Tumble(deltaYaw, deltaPitch);
|
||||
}
|
||||
|
||||
private:
|
||||
void PushToCameraTransform();
|
||||
void PullFromCameraTransform();
|
||||
void SetClippingPlanes(const pxr::GfBBox3d& stageBBox);
|
||||
void ResetClippingPlanes();
|
||||
std::pair<double,double> RangeOfBoxAlongRay(
|
||||
const pxr::GfRay& camRay, const pxr::GfBBox3d& bbox) const;
|
||||
|
||||
// Core camera object (owns aperture/projection/clipping/transform state)
|
||||
pxr::GfCamera m_camera;
|
||||
bool m_cameraTransformDirty;
|
||||
|
||||
// Orbital / tumble state
|
||||
double m_rotTheta; // horizontal orbit (degrees, around Y)
|
||||
double m_rotPhi; // vertical orbit (degrees, around X)
|
||||
double m_rotPsi; // roll (degrees, usually 0)
|
||||
pxr::GfVec3d m_center; // look-at center in world space
|
||||
double m_dist; // distance camera → center
|
||||
double m_selSize; // extent of last framed selection
|
||||
|
||||
// Stage up-axis handling
|
||||
bool m_isZUp;
|
||||
pxr::GfMatrix4d m_YZUpMatrix; // Y-up → Z-up (rotate -90° around X)
|
||||
pxr::GfMatrix4d m_YZUpInvMatrix; // Z-up → Y-up (inverse)
|
||||
|
||||
// Auto-clip state
|
||||
bool m_hasClosestVisibleDist;
|
||||
double m_closestVisibleDist;
|
||||
double m_lastFramedDist;
|
||||
double m_lastFramedClosestDist;
|
||||
double m_overrideNear; // ≤0 means "no override"
|
||||
double m_overrideFar; // ≤0 means "no override"
|
||||
|
||||
// Camera mode
|
||||
CameraMode m_mode;
|
||||
pxr::SdfPath m_usdCameraPath;
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "AddReferenceCommand.h"
|
||||
#include "../../utils/Logger.h"
|
||||
#include <pxr/usd/usd/references.h>
|
||||
#include <pxr/base/tf/token.h>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
AddReferenceCommand::AddReferenceCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& xformPath,
|
||||
const std::string& refFilePath)
|
||||
: m_stage(stage)
|
||||
, m_xformPath(xformPath)
|
||||
, m_refFilePath(refFilePath)
|
||||
, m_description("Add Reference " + refFilePath)
|
||||
{
|
||||
}
|
||||
|
||||
void AddReferenceCommand::Execute() {
|
||||
if (!m_stage) return;
|
||||
try {
|
||||
pxr::UsdPrim xformPrim = m_stage->DefinePrim(m_xformPath, pxr::TfToken("Xform"));
|
||||
if (!xformPrim.IsValid()) {
|
||||
LOG_ERROR("AddReferenceCommand: failed to define Xform at " + m_xformPath.GetString());
|
||||
return;
|
||||
}
|
||||
if (!xformPrim.GetReferences().AddReference(m_refFilePath))
|
||||
LOG_ERROR("AddReferenceCommand: failed to add reference " + m_refFilePath);
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("AddReferenceCommand::Execute error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void AddReferenceCommand::Undo() {
|
||||
if (!m_stage) return;
|
||||
try {
|
||||
if (!m_stage->RemovePrim(m_xformPath))
|
||||
LOG_ERROR("AddReferenceCommand::Undo: failed to remove prim " + m_xformPath.GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("AddReferenceCommand::Undo error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "../CommandHistory.h"
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/sdf/reference.h>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
/// Wraps adding an Xform prim + reference (stage-level "Add Reference..." action).
|
||||
class AddReferenceCommand : public ICommand {
|
||||
public:
|
||||
AddReferenceCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& xformPath,
|
||||
const std::string& refFilePath);
|
||||
|
||||
void Execute() override;
|
||||
void Undo() override;
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
pxr::SdfPath m_xformPath;
|
||||
std::string m_refFilePath;
|
||||
std::string m_description;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "AttributeSetCommand.h"
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
AttributeSetCommand::AttributeSetCommand(std::string description,
|
||||
std::function<void()> executeFunc,
|
||||
std::function<void()> undoFunc)
|
||||
: m_description(std::move(description))
|
||||
, m_executeFunc(std::move(executeFunc))
|
||||
, m_undoFunc(std::move(undoFunc))
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "../CommandHistory.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
/// Type-agnostic attribute set command.
|
||||
/// Stores execute and undo as std::function closures capturing the typed values.
|
||||
class AttributeSetCommand : public ICommand {
|
||||
public:
|
||||
AttributeSetCommand(std::string description,
|
||||
std::function<void()> executeFunc,
|
||||
std::function<void()> undoFunc);
|
||||
|
||||
void Execute() override { if (m_executeFunc) m_executeFunc(); }
|
||||
void Undo() override { if (m_undoFunc) m_undoFunc(); }
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
std::string m_description;
|
||||
std::function<void()> m_executeFunc;
|
||||
std::function<void()> m_undoFunc;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "CreatePrimCommand.h"
|
||||
#include "../../utils/Logger.h"
|
||||
#include <pxr/usd/usd/prim.h>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
CreatePrimCommand::CreatePrimCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
const pxr::TfToken& typeName)
|
||||
: m_stage(stage)
|
||||
, m_primPath(primPath)
|
||||
, m_typeName(typeName)
|
||||
, m_description("Create " + typeName.GetString() + " " + primPath.GetString())
|
||||
{
|
||||
}
|
||||
|
||||
void CreatePrimCommand::Execute() {
|
||||
if (!m_stage) return;
|
||||
try {
|
||||
pxr::UsdPrim prim = m_stage->DefinePrim(m_primPath, m_typeName);
|
||||
if (!prim.IsValid())
|
||||
LOG_ERROR("CreatePrimCommand: failed to define prim " + m_primPath.GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("CreatePrimCommand::Execute error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CreatePrimCommand::Undo() {
|
||||
if (!m_stage) return;
|
||||
try {
|
||||
if (!m_stage->RemovePrim(m_primPath))
|
||||
LOG_ERROR("CreatePrimCommand: failed to remove prim " + m_primPath.GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("CreatePrimCommand::Undo error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "../CommandHistory.h"
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <pxr/base/tf/token.h>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
class CreatePrimCommand : public ICommand {
|
||||
public:
|
||||
CreatePrimCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
const pxr::TfToken& typeName);
|
||||
|
||||
void Execute() override;
|
||||
void Undo() override;
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
pxr::SdfPath m_primPath;
|
||||
pxr::TfToken m_typeName;
|
||||
std::string m_description;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "DeletePrimCommand.h"
|
||||
#include "../../utils/Logger.h"
|
||||
#include <pxr/usd/sdf/copyUtils.h>
|
||||
#include <pxr/usd/sdf/primSpec.h>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
DeletePrimCommand::DeletePrimCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath)
|
||||
: m_stage(stage)
|
||||
, m_primPath(primPath)
|
||||
, m_description("Delete " + primPath.GetString())
|
||||
{
|
||||
if (!stage) return;
|
||||
pxr::SdfLayerHandle rootLayer = stage->GetRootLayer();
|
||||
if (!rootLayer) return;
|
||||
|
||||
// Only capture if the spec exists on the root layer.
|
||||
if (!rootLayer->HasSpec(primPath)) {
|
||||
LOG_ERROR("DeletePrimCommand: no spec found for " + primPath.GetString());
|
||||
return;
|
||||
}
|
||||
|
||||
m_savedLayer = pxr::SdfLayer::CreateAnonymous(".usda");
|
||||
if (!m_savedLayer) return;
|
||||
|
||||
// Ensure the parent path hierarchy exists in the saved layer.
|
||||
pxr::SdfPath parentPath = primPath.GetParentPath();
|
||||
while (!parentPath.IsAbsoluteRootPath() && !m_savedLayer->HasSpec(parentPath)) {
|
||||
pxr::SdfPrimSpec::New(m_savedLayer,
|
||||
parentPath.GetName(),
|
||||
pxr::SdfSpecifierOver);
|
||||
parentPath = parentPath.GetParentPath();
|
||||
}
|
||||
|
||||
if (pxr::SdfCopySpec(rootLayer, primPath, m_savedLayer, primPath)) {
|
||||
m_specWasSaved = true;
|
||||
} else {
|
||||
LOG_ERROR("DeletePrimCommand: SdfCopySpec failed for " + primPath.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
void DeletePrimCommand::Execute() {
|
||||
if (!m_stage) return;
|
||||
try {
|
||||
if (!m_stage->RemovePrim(m_primPath))
|
||||
LOG_ERROR("DeletePrimCommand: RemovePrim failed for " + m_primPath.GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("DeletePrimCommand::Execute error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void DeletePrimCommand::Undo() {
|
||||
if (!m_stage || !m_specWasSaved || !m_savedLayer) {
|
||||
LOG_ERROR("DeletePrimCommand::Undo: cannot restore — spec was not saved");
|
||||
return;
|
||||
}
|
||||
pxr::SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
if (!rootLayer) return;
|
||||
|
||||
try {
|
||||
if (!pxr::SdfCopySpec(m_savedLayer, m_primPath, rootLayer, m_primPath))
|
||||
LOG_ERROR("DeletePrimCommand::Undo: SdfCopySpec failed for " + m_primPath.GetString());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("DeletePrimCommand::Undo error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "../CommandHistory.h"
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
/// Saves the prim spec via SdfCopySpec into an anonymous in-memory layer
|
||||
/// before deletion; restores it on Undo.
|
||||
class DeletePrimCommand : public ICommand {
|
||||
public:
|
||||
/// Captures the current spec of @p primPath from the root layer into an
|
||||
/// anonymous layer. Must be constructed BEFORE the prim is removed.
|
||||
DeletePrimCommand(pxr::UsdStageRefPtr stage, const pxr::SdfPath& primPath);
|
||||
|
||||
void Execute() override;
|
||||
void Undo() override;
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
pxr::SdfPath m_primPath;
|
||||
pxr::SdfLayerRefPtr m_savedLayer; ///< anonymous layer holding the spec snapshot
|
||||
std::string m_description;
|
||||
bool m_specWasSaved = false;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "LayerCommands.h"
|
||||
#include "../../utils/Logger.h"
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
// ─── LayerCreateCommand ───────────────────────────────────────────────────────
|
||||
|
||||
LayerCreateCommand::LayerCreateCommand(LayerManager* mgr, std::string path, int index)
|
||||
: m_mgr(mgr)
|
||||
, m_path(std::move(path))
|
||||
, m_index(index)
|
||||
, m_description("Create Layer " + m_path)
|
||||
{
|
||||
}
|
||||
|
||||
void LayerCreateCommand::Execute() {
|
||||
if (!m_mgr) return;
|
||||
// Record how many sublayers exist before insert to compute the real index used.
|
||||
auto before = m_mgr->GetLayerStack().size();
|
||||
m_mgr->CreateSublayer(m_path, m_index);
|
||||
auto after = m_mgr->GetLayerStack().size();
|
||||
// If a layer was actually inserted, compute its index.
|
||||
if (after > before) {
|
||||
auto layers = m_mgr->GetLayerStack();
|
||||
for (int i = 0; i < static_cast<int>(layers.size()); ++i) {
|
||||
if (layers[i].identifier == m_path || layers[i].displayName == m_path) {
|
||||
m_insertedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LayerCreateCommand::Undo() {
|
||||
if (!m_mgr) return;
|
||||
if (m_insertedIndex >= 0) {
|
||||
m_mgr->RemoveSublayer(m_insertedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── LayerRemoveCommand ───────────────────────────────────────────────────────
|
||||
|
||||
LayerRemoveCommand::LayerRemoveCommand(LayerManager* mgr, int index)
|
||||
: m_mgr(mgr)
|
||||
, m_index(index)
|
||||
, m_description("Remove Layer")
|
||||
{
|
||||
if (!mgr) return;
|
||||
auto layers = mgr->GetLayerStack();
|
||||
if (index >= 0 && index < static_cast<int>(layers.size())) {
|
||||
m_savedPath = layers[index].identifier;
|
||||
m_description = "Remove Layer " + layers[index].displayName;
|
||||
}
|
||||
}
|
||||
|
||||
void LayerRemoveCommand::Execute() {
|
||||
if (!m_mgr) return;
|
||||
m_mgr->RemoveSublayer(m_index);
|
||||
}
|
||||
|
||||
void LayerRemoveCommand::Undo() {
|
||||
if (!m_mgr || m_savedPath.empty()) return;
|
||||
// Re-insert at the original index.
|
||||
m_mgr->InsertSublayerPath(m_savedPath, m_index);
|
||||
}
|
||||
|
||||
// ─── LayerReorderCommand ──────────────────────────────────────────────────────
|
||||
|
||||
LayerReorderCommand::LayerReorderCommand(LayerManager* mgr,
|
||||
std::vector<std::string> before,
|
||||
std::vector<std::string> after)
|
||||
: m_mgr(mgr)
|
||||
, m_before(std::move(before))
|
||||
, m_after(std::move(after))
|
||||
, m_description("Reorder Layers")
|
||||
{
|
||||
}
|
||||
|
||||
void LayerReorderCommand::Execute() { ApplyOrder(m_after); }
|
||||
void LayerReorderCommand::Undo() { ApplyOrder(m_before); }
|
||||
|
||||
void LayerReorderCommand::ApplyOrder(const std::vector<std::string>& order) {
|
||||
if (!m_mgr) return;
|
||||
// Remove all sublayers and re-insert in the desired order.
|
||||
// We only control sublayers — root and session are fixed.
|
||||
// First gather current sublayer indices (non-root, non-session).
|
||||
auto layers = m_mgr->GetLayerStack();
|
||||
|
||||
// Count sublayers and remove them from highest index down.
|
||||
std::vector<int> sublayerIndices;
|
||||
for (int i = 0; i < static_cast<int>(layers.size()); ++i) {
|
||||
if (!layers[i].isRootLayer && !layers[i].isSessionLayer)
|
||||
sublayerIndices.push_back(i);
|
||||
}
|
||||
// Remove from back to front to preserve indices.
|
||||
for (int i = static_cast<int>(sublayerIndices.size()) - 1; i >= 0; --i)
|
||||
m_mgr->RemoveSublayer(sublayerIndices[i]);
|
||||
|
||||
// Re-add in desired order.
|
||||
for (const auto& path : order)
|
||||
m_mgr->InsertSublayerPath(path, -1);
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "../CommandHistory.h"
|
||||
#include "../LayerManager.h"
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
class LayerCreateCommand : public ICommand {
|
||||
public:
|
||||
LayerCreateCommand(LayerManager* mgr, std::string path, int index = -1);
|
||||
void Execute() override;
|
||||
void Undo() override;
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
LayerManager* m_mgr;
|
||||
std::string m_path;
|
||||
int m_index;
|
||||
int m_insertedIndex = -1;
|
||||
std::string m_description;
|
||||
};
|
||||
|
||||
class LayerRemoveCommand : public ICommand {
|
||||
public:
|
||||
LayerRemoveCommand(LayerManager* mgr, int index);
|
||||
void Execute() override;
|
||||
void Undo() override;
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
LayerManager* m_mgr;
|
||||
int m_index;
|
||||
std::string m_savedPath;
|
||||
std::string m_description;
|
||||
};
|
||||
|
||||
class LayerReorderCommand : public ICommand {
|
||||
public:
|
||||
LayerReorderCommand(LayerManager* mgr, std::vector<std::string> before, std::vector<std::string> after);
|
||||
void Execute() override;
|
||||
void Undo() override;
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
void ApplyOrder(const std::vector<std::string>& order);
|
||||
|
||||
LayerManager* m_mgr;
|
||||
std::vector<std::string> m_before;
|
||||
std::vector<std::string> m_after;
|
||||
std::string m_description;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "ReplaceReferenceCommand.h"
|
||||
#include "../../utils/Logger.h"
|
||||
#include <pxr/usd/usd/references.h>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
ReplaceReferenceCommand::ReplaceReferenceCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
const pxr::SdfReference& oldRef,
|
||||
const pxr::SdfReference& newRef)
|
||||
: m_stage(stage)
|
||||
, m_primPath(primPath)
|
||||
, m_oldRef(oldRef)
|
||||
, m_newRef(newRef)
|
||||
, m_description("Replace Reference on " + primPath.GetString())
|
||||
{
|
||||
}
|
||||
|
||||
void ReplaceReferenceCommand::Execute() { Apply(m_oldRef, m_newRef); }
|
||||
void ReplaceReferenceCommand::Undo() { Apply(m_newRef, m_oldRef); }
|
||||
|
||||
void ReplaceReferenceCommand::Apply(const pxr::SdfReference& toRemove,
|
||||
const pxr::SdfReference& toAdd)
|
||||
{
|
||||
if (!m_stage) return;
|
||||
pxr::UsdPrim prim = m_stage->GetPrimAtPath(m_primPath);
|
||||
if (!prim.IsValid()) {
|
||||
LOG_ERROR("ReplaceReferenceCommand::Apply: prim not valid " + m_primPath.GetString());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
pxr::UsdReferences refs = prim.GetReferences();
|
||||
if (!refs.RemoveReference(toRemove))
|
||||
LOG_ERROR("ReplaceReferenceCommand: failed to remove reference");
|
||||
if (!refs.AddReference(toAdd))
|
||||
LOG_ERROR("ReplaceReferenceCommand: failed to add reference");
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("ReplaceReferenceCommand::Apply error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "../CommandHistory.h"
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/sdf/reference.h>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
class ReplaceReferenceCommand : public ICommand {
|
||||
public:
|
||||
ReplaceReferenceCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
const pxr::SdfReference& oldRef,
|
||||
const pxr::SdfReference& newRef);
|
||||
|
||||
void Execute() override;
|
||||
void Undo() override;
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
void Apply(const pxr::SdfReference& toRemove, const pxr::SdfReference& toAdd);
|
||||
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
pxr::SdfPath m_primPath;
|
||||
pxr::SdfReference m_oldRef;
|
||||
pxr::SdfReference m_newRef;
|
||||
std::string m_description;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "TransformCommand.h"
|
||||
#include "../../utils/Logger.h"
|
||||
#include <pxr/usd/usd/editContext.h>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
TransformCommand::TransformCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
pxr::SdfLayerHandle editLayer,
|
||||
const pxr::GfVec3d& oldTranslate,
|
||||
const pxr::GfVec3f& oldRotate,
|
||||
const pxr::GfVec3f& oldScale,
|
||||
const pxr::GfVec3d& newTranslate,
|
||||
const pxr::GfVec3f& newRotate,
|
||||
const pxr::GfVec3f& newScale,
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder,
|
||||
std::string description)
|
||||
: m_stage(stage)
|
||||
, m_primPath(primPath)
|
||||
, m_editLayer(editLayer)
|
||||
, m_oldTranslate(oldTranslate)
|
||||
, m_oldRotate(oldRotate)
|
||||
, m_oldScale(oldScale)
|
||||
, m_newTranslate(newTranslate)
|
||||
, m_newRotate(newRotate)
|
||||
, m_newScale(newScale)
|
||||
, m_rotOrder(rotOrder)
|
||||
, m_description(std::move(description))
|
||||
{
|
||||
}
|
||||
|
||||
void TransformCommand::Execute() {
|
||||
Apply(m_newTranslate, m_newRotate, m_newScale);
|
||||
}
|
||||
|
||||
void TransformCommand::Undo() {
|
||||
Apply(m_oldTranslate, m_oldRotate, m_oldScale);
|
||||
}
|
||||
|
||||
void TransformCommand::Apply(const pxr::GfVec3d& t,
|
||||
const pxr::GfVec3f& r,
|
||||
const pxr::GfVec3f& s)
|
||||
{
|
||||
if (!m_stage || m_primPath.IsEmpty()) return;
|
||||
pxr::UsdPrim prim = m_stage->GetPrimAtPath(m_primPath);
|
||||
if (!prim) return;
|
||||
|
||||
pxr::UsdGeomXformCommonAPI api(prim);
|
||||
if (!api) return;
|
||||
|
||||
try {
|
||||
if (m_editLayer) {
|
||||
pxr::UsdEditContext ec(m_stage, m_editLayer);
|
||||
api.SetTranslate(t, pxr::UsdTimeCode::Default());
|
||||
api.SetRotate(r, m_rotOrder, pxr::UsdTimeCode::Default());
|
||||
api.SetScale(s, pxr::UsdTimeCode::Default());
|
||||
} else {
|
||||
api.SetTranslate(t, pxr::UsdTimeCode::Default());
|
||||
api.SetRotate(r, m_rotOrder, pxr::UsdTimeCode::Default());
|
||||
api.SetScale(s, pxr::UsdTimeCode::Default());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("TransformCommand::Apply error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "../CommandHistory.h"
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <pxr/base/gf/vec3d.h>
|
||||
#include <pxr/base/gf/vec3f.h>
|
||||
#include <pxr/usd/usdGeom/xformCommonAPI.h>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
/// Reverses a complete TRS edit on a single prim (from gizmo drag or
|
||||
/// Property panel field commit). Stores the prim path, edit-target layer,
|
||||
/// and pre/post translate/rotate/scale triples.
|
||||
class TransformCommand : public ICommand {
|
||||
public:
|
||||
TransformCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
pxr::SdfLayerHandle editLayer,
|
||||
const pxr::GfVec3d& oldTranslate,
|
||||
const pxr::GfVec3f& oldRotate,
|
||||
const pxr::GfVec3f& oldScale,
|
||||
const pxr::GfVec3d& newTranslate,
|
||||
const pxr::GfVec3f& newRotate,
|
||||
const pxr::GfVec3f& newScale,
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder,
|
||||
std::string description = "Transform");
|
||||
|
||||
void Execute() override;
|
||||
void Undo() override;
|
||||
std::string GetDescription() const override { return m_description; }
|
||||
|
||||
private:
|
||||
void Apply(const pxr::GfVec3d& t,
|
||||
const pxr::GfVec3f& r,
|
||||
const pxr::GfVec3f& s);
|
||||
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
pxr::SdfPath m_primPath;
|
||||
pxr::SdfLayerHandle m_editLayer;
|
||||
|
||||
pxr::GfVec3d m_oldTranslate;
|
||||
pxr::GfVec3f m_oldRotate;
|
||||
pxr::GfVec3f m_oldScale;
|
||||
|
||||
pxr::GfVec3d m_newTranslate;
|
||||
pxr::GfVec3f m_newRotate;
|
||||
pxr::GfVec3f m_newScale;
|
||||
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder m_rotOrder;
|
||||
std::string m_description;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
Reference in New Issue
Block a user