Timeline Prototype
This commit is contained in:
@@ -356,7 +356,7 @@ pxr::GfRange3d UsdSceneRenderer::ComputeStageBounds()
|
||||
if (!m_stage) return {};
|
||||
pxr::TfTokenVector purposes = {
|
||||
pxr::UsdGeomTokens->default_, pxr::UsdGeomTokens->proxy };
|
||||
pxr::UsdGeomBBoxCache bboxCache(pxr::UsdTimeCode::Default(), purposes, true);
|
||||
pxr::UsdGeomBBoxCache bboxCache(m_currentTime, purposes, true);
|
||||
return bboxCache.ComputeWorldBound(m_stage->GetPseudoRoot()).ComputeAlignedRange();
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ void UsdSceneRenderer::Render(int width, int height)
|
||||
|
||||
// --- Render params (matches stageView.renderSinglePass) ---
|
||||
m_renderParams = pxr::UsdImagingGLRenderParams();
|
||||
m_renderParams.frame = pxr::UsdTimeCode::Default();
|
||||
m_renderParams.frame = m_currentTime;
|
||||
m_renderParams.complexity = 1.0f;
|
||||
m_renderParams.drawMode = pxr::UsdImagingGLDrawMode::DRAW_SHADED_SMOOTH;
|
||||
m_renderParams.showGuides = true;
|
||||
@@ -1101,7 +1101,7 @@ void UsdSceneRenderer::DrawBoundingBoxes(
|
||||
pxr::TfTokenVector purposes = {
|
||||
pxr::UsdGeomTokens->default_, pxr::UsdGeomTokens->proxy };
|
||||
pxr::UsdGeomBBoxCache bboxCache(
|
||||
pxr::UsdTimeCode::Default(), purposes, /*useExtentsHint=*/true);
|
||||
m_currentTime, purposes, /*useExtentsHint=*/true);
|
||||
|
||||
// Save/restore GL state
|
||||
GLboolean prevDepthMask;
|
||||
@@ -1350,7 +1350,7 @@ void UsdSceneRenderer::DrawCameraWireframes(
|
||||
if (!prim) continue;
|
||||
|
||||
pxr::UsdGeomCamera usdCam(prim);
|
||||
pxr::GfCamera gfCam = usdCam.GetCamera(pxr::UsdTimeCode::Default());
|
||||
pxr::GfCamera gfCam = usdCam.GetCamera(m_currentTime);
|
||||
|
||||
// Determine colour
|
||||
bool isSelected = (selectedSet.count(camPath.GetString()) > 0);
|
||||
@@ -1430,7 +1430,7 @@ bool UsdSceneRenderer::PickCameraAtPoint(
|
||||
if (!prim) continue;
|
||||
|
||||
pxr::UsdGeomCamera usdCam(prim);
|
||||
pxr::GfCamera gfCam = usdCam.GetCamera(pxr::UsdTimeCode::Default());
|
||||
pxr::GfCamera gfCam = usdCam.GetCamera(m_currentTime);
|
||||
|
||||
std::vector<float> verts;
|
||||
BuildCameraWireframeLines(gfCam, scale, verts);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/usd/timeCode.h>
|
||||
#include <pxr/imaging/glf/drawTarget.h>
|
||||
#include <pxr/usdImaging/usdImagingGL/engine.h>
|
||||
#include <pxr/imaging/glf/simpleLight.h>
|
||||
@@ -163,6 +164,11 @@ public:
|
||||
if (val) m_cameraCacheDirty = true;
|
||||
}
|
||||
|
||||
/// Current evaluation time (timeline frame). Drives Hydra rendering,
|
||||
/// USD camera resolution, and bounding-box computation.
|
||||
pxr::UsdTimeCode GetCurrentTimeCode() const { return m_currentTime; }
|
||||
void SetCurrentTimeCode(pxr::UsdTimeCode t) { m_currentTime = t; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Bounding box display
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -267,6 +273,8 @@ private:
|
||||
bool m_rendererInitialized;
|
||||
bool m_forceRefresh;
|
||||
|
||||
pxr::UsdTimeCode m_currentTime = pxr::UsdTimeCode::Default();
|
||||
|
||||
int m_diagFrameCount;
|
||||
|
||||
// --- Grid GL resources (line geometry, reuses m_axisProgram / m_axisUniformMVP) ---
|
||||
|
||||
@@ -1,9 +1,52 @@
|
||||
#include "TransformCommand.h"
|
||||
#include "../../utils/Logger.h"
|
||||
#include <pxr/usd/usd/editContext.h>
|
||||
#include <pxr/usd/usdGeom/xformOp.h>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
// True when attr has an authored time sample exactly at time t.
|
||||
static bool HasSampleAtTime(const pxr::UsdAttribute& attr, double t)
|
||||
{
|
||||
if (!attr) return false;
|
||||
std::vector<double> times;
|
||||
if (!attr.GetTimeSamples(×)) return false;
|
||||
for (double s : times)
|
||||
if (s == t) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attribute name of the rotate op XformCommonAPI authors for a rotation order
|
||||
// (e.g. "xformOp:rotateXYZ").
|
||||
static pxr::TfToken RotateOpName(pxr::UsdGeomXformCommonAPI::RotationOrder ro)
|
||||
{
|
||||
return pxr::UsdGeomXformOp::GetOpName(
|
||||
pxr::UsdGeomXformCommonAPI::ConvertRotationOrderToOpType(ro));
|
||||
}
|
||||
|
||||
TransformCommand::SamplePresence TransformCommand::QuerySamplePresence(
|
||||
pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder,
|
||||
pxr::UsdTimeCode timeCode)
|
||||
{
|
||||
SamplePresence presence;
|
||||
if (timeCode.IsDefault() || !stage) return presence;
|
||||
pxr::UsdPrim prim = stage->GetPrimAtPath(primPath);
|
||||
if (!prim) return presence;
|
||||
|
||||
double t = timeCode.GetValue();
|
||||
presence.translate = HasSampleAtTime(
|
||||
prim.GetAttribute(pxr::UsdGeomXformOp::GetOpName(
|
||||
pxr::UsdGeomXformOp::TypeTranslate)), t);
|
||||
presence.rotate = HasSampleAtTime(
|
||||
prim.GetAttribute(RotateOpName(rotOrder)), t);
|
||||
presence.scale = HasSampleAtTime(
|
||||
prim.GetAttribute(pxr::UsdGeomXformOp::GetOpName(
|
||||
pxr::UsdGeomXformOp::TypeScale)), t);
|
||||
return presence;
|
||||
}
|
||||
|
||||
TransformCommand::TransformCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
pxr::SdfLayerHandle editLayer,
|
||||
@@ -14,6 +57,8 @@ TransformCommand::TransformCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::GfVec3f& newRotate,
|
||||
const pxr::GfVec3f& newScale,
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder,
|
||||
pxr::UsdTimeCode timeCode,
|
||||
SamplePresence preEditSamples,
|
||||
std::string description)
|
||||
: m_stage(stage)
|
||||
, m_primPath(primPath)
|
||||
@@ -25,7 +70,9 @@ TransformCommand::TransformCommand(pxr::UsdStageRefPtr stage,
|
||||
, m_newRotate(newRotate)
|
||||
, m_newScale(newScale)
|
||||
, m_rotOrder(rotOrder)
|
||||
, m_timeCode(timeCode)
|
||||
, m_description(std::move(description))
|
||||
, m_preEditSamples(preEditSamples)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -35,6 +82,7 @@ void TransformCommand::Execute() {
|
||||
|
||||
void TransformCommand::Undo() {
|
||||
Apply(m_oldTranslate, m_oldRotate, m_oldScale);
|
||||
ClearCreatedSamples();
|
||||
}
|
||||
|
||||
void TransformCommand::Apply(const pxr::GfVec3d& t,
|
||||
@@ -51,17 +99,50 @@ void TransformCommand::Apply(const pxr::GfVec3d& t,
|
||||
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());
|
||||
api.SetTranslate(t, m_timeCode);
|
||||
api.SetRotate(r, m_rotOrder, m_timeCode);
|
||||
api.SetScale(s, m_timeCode);
|
||||
} else {
|
||||
api.SetTranslate(t, pxr::UsdTimeCode::Default());
|
||||
api.SetRotate(r, m_rotOrder, pxr::UsdTimeCode::Default());
|
||||
api.SetScale(s, pxr::UsdTimeCode::Default());
|
||||
api.SetTranslate(t, m_timeCode);
|
||||
api.SetRotate(r, m_rotOrder, m_timeCode);
|
||||
api.SetScale(s, m_timeCode);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("TransformCommand::Apply error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void TransformCommand::ClearCreatedSamples()
|
||||
{
|
||||
if (m_timeCode.IsDefault()) return;
|
||||
if (!m_stage || m_primPath.IsEmpty()) return;
|
||||
pxr::UsdPrim prim = m_stage->GetPrimAtPath(m_primPath);
|
||||
if (!prim) return;
|
||||
|
||||
auto clearOp = [&](const pxr::TfToken& opName, bool hadSample) {
|
||||
if (hadSample) return;
|
||||
pxr::UsdAttribute attr = prim.GetAttribute(opName);
|
||||
if (attr) attr.ClearAtTime(m_timeCode);
|
||||
};
|
||||
|
||||
auto clearAll = [&]() {
|
||||
clearOp(pxr::UsdGeomXformOp::GetOpName(
|
||||
pxr::UsdGeomXformOp::TypeTranslate), m_preEditSamples.translate);
|
||||
clearOp(RotateOpName(m_rotOrder), m_preEditSamples.rotate);
|
||||
clearOp(pxr::UsdGeomXformOp::GetOpName(
|
||||
pxr::UsdGeomXformOp::TypeScale), m_preEditSamples.scale);
|
||||
};
|
||||
|
||||
try {
|
||||
if (m_editLayer) {
|
||||
pxr::UsdEditContext ec(m_stage, m_editLayer);
|
||||
clearAll();
|
||||
} else {
|
||||
clearAll();
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("TransformCommand::ClearCreatedSamples error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../CommandHistory.h"
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/usd/timeCode.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <pxr/base/gf/vec3d.h>
|
||||
@@ -14,8 +15,26 @@ 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.
|
||||
///
|
||||
/// When timeCode is not Default() the edit is a keyframe: values are written
|
||||
/// as time samples at that frame. Undo removes samples this command created
|
||||
/// on ops that had no sample at that frame before the edit.
|
||||
class TransformCommand : public ICommand {
|
||||
public:
|
||||
/// Whether each common op had an authored sample at the edit time.
|
||||
/// Must be queried BEFORE the first live write of an interactive edit
|
||||
/// (live writes author the sample, so querying later always finds one).
|
||||
struct SamplePresence {
|
||||
bool translate = false;
|
||||
bool rotate = false;
|
||||
bool scale = false;
|
||||
};
|
||||
static SamplePresence QuerySamplePresence(
|
||||
pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder,
|
||||
pxr::UsdTimeCode timeCode);
|
||||
|
||||
TransformCommand(pxr::UsdStageRefPtr stage,
|
||||
const pxr::SdfPath& primPath,
|
||||
pxr::SdfLayerHandle editLayer,
|
||||
@@ -26,6 +45,8 @@ public:
|
||||
const pxr::GfVec3f& newRotate,
|
||||
const pxr::GfVec3f& newScale,
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder rotOrder,
|
||||
pxr::UsdTimeCode timeCode,
|
||||
SamplePresence preEditSamples,
|
||||
std::string description = "Transform");
|
||||
|
||||
void Execute() override;
|
||||
@@ -36,6 +57,9 @@ private:
|
||||
void Apply(const pxr::GfVec3d& t,
|
||||
const pxr::GfVec3f& r,
|
||||
const pxr::GfVec3f& s);
|
||||
/// Remove the time samples Execute created on ops that previously had
|
||||
/// no sample at m_timeCode (no-op when m_timeCode is Default).
|
||||
void ClearCreatedSamples();
|
||||
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
pxr::SdfPath m_primPath;
|
||||
@@ -50,7 +74,12 @@ private:
|
||||
pxr::GfVec3f m_newScale;
|
||||
|
||||
pxr::UsdGeomXformCommonAPI::RotationOrder m_rotOrder;
|
||||
pxr::UsdTimeCode m_timeCode;
|
||||
std::string m_description;
|
||||
|
||||
// Whether each op already had a sample at m_timeCode before the edit
|
||||
// (only meaningful when m_timeCode is not Default).
|
||||
SamplePresence m_preEditSamples;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
|
||||
Reference in New Issue
Block a user