Init Repo

This commit is contained in:
2026-06-03 09:00:11 +08:00
commit 9be48d8b9e
155 changed files with 14827 additions and 0 deletions
+59
View File
@@ -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
+58
View File
@@ -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
+215
View File
@@ -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
+57
View File
@@ -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
+223
View File
@@ -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
+66
View File
@@ -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
+307
View File
@@ -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
+248
View File
@@ -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
+45
View File
@@ -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
+384
View File
@@ -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
+175
View File
@@ -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
+43
View File
@@ -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
+29
View File
@@ -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
+14
View File
@@ -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
+27
View File
@@ -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
+38
View File
@@ -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
+29
View File
@@ -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
+69
View File
@@ -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
+31
View File
@@ -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
+104
View File
@@ -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
+54
View File
@@ -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
+67
View File
@@ -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
+56
View File
@@ -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