Timeline Prototype
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
#include "TimelinePanel.h"
|
||||
|
||||
#include <pxr/usd/usd/editContext.h>
|
||||
#include <imgui.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void TimelinePanel::SetStage(pxr::UsdStageRefPtr stage)
|
||||
{
|
||||
m_stage = stage;
|
||||
m_playing = false;
|
||||
|
||||
m_startFrame = 1.0;
|
||||
m_endFrame = 100.0;
|
||||
m_fps = 24.0;
|
||||
if (stage) {
|
||||
if (stage->HasAuthoredTimeCodeRange()) {
|
||||
m_startFrame = stage->GetStartTimeCode();
|
||||
m_endFrame = stage->GetEndTimeCode();
|
||||
}
|
||||
double tps = stage->GetTimeCodesPerSecond();
|
||||
if (tps > 0.0) m_fps = tps;
|
||||
}
|
||||
if (m_endFrame < m_startFrame) m_endFrame = m_startFrame;
|
||||
|
||||
// Always notify on stage change so consumers pick up the new range,
|
||||
// even when the numeric frame happens to be unchanged.
|
||||
m_currentFrame = m_startFrame;
|
||||
NotifyTimeChanged();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void TimelinePanel::Update(float deltaTime)
|
||||
{
|
||||
if (!m_playing) return;
|
||||
double next = m_currentFrame + double(deltaTime) * m_fps;
|
||||
if (next > m_endFrame) {
|
||||
double span = m_endFrame - m_startFrame;
|
||||
next = (span > 0.0)
|
||||
? m_startFrame + std::fmod(next - m_startFrame, span)
|
||||
: m_startFrame;
|
||||
}
|
||||
SetCurrentFrame(next);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void TimelinePanel::Render()
|
||||
{
|
||||
// ---- Transport row: icon buttons | start/end range | fps | auto-key ----
|
||||
auto iconBtn = [&](const char* id, Icon icon, const char* fallback) -> bool {
|
||||
if (m_iconManager) {
|
||||
ImTextureID tex = m_iconManager->Get(icon);
|
||||
return ImGui::ImageButton(id, ImTextureRef(tex), ImVec2(18.f, 18.f));
|
||||
}
|
||||
return ImGui::Button(fallback);
|
||||
};
|
||||
|
||||
if (iconBtn("##skipback", Icon::SkipBack, "|<")) { m_playing = false; SetCurrentFrame(m_startFrame); }
|
||||
ImGui::SameLine();
|
||||
if (iconBtn("##stepback", Icon::StepBack, "<")) { m_playing = false; SetCurrentFrame(std::floor(m_currentFrame) - 1.0); }
|
||||
ImGui::SameLine();
|
||||
if (iconBtn("##playpause", m_playing ? Icon::Pause : Icon::Play, m_playing ? "Pause" : "Play")) m_playing = !m_playing;
|
||||
ImGui::SameLine();
|
||||
if (iconBtn("##stepfwd", Icon::StepForward, ">")) { m_playing = false; SetCurrentFrame(std::floor(m_currentFrame) + 1.0); }
|
||||
ImGui::SameLine();
|
||||
if (iconBtn("##skipend", Icon::SkipEnd, ">|")) { m_playing = false; SetCurrentFrame(m_endFrame); }
|
||||
|
||||
ImGui::SameLine();
|
||||
int start = int(std::lround(m_startFrame));
|
||||
int end = int(std::lround(m_endFrame));
|
||||
ImGui::SetNextItemWidth(90.f);
|
||||
bool rangeEdited = ImGui::DragInt("##start", &start, 1.0f, 0, 0, "Start %d");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(90.f);
|
||||
rangeEdited |= ImGui::DragInt("##end", &end, 1.0f, 0, 0, "End %d");
|
||||
if (rangeEdited) {
|
||||
if (end < start) end = start;
|
||||
m_startFrame = double(start);
|
||||
m_endFrame = double(end);
|
||||
if (m_stage) {
|
||||
// Stage time metadata must live on the root (or session) layer;
|
||||
// the active edit target may be a sublayer.
|
||||
pxr::UsdEditContext ec(m_stage, m_stage->GetRootLayer());
|
||||
m_stage->SetStartTimeCode(m_startFrame);
|
||||
m_stage->SetEndTimeCode(m_endFrame);
|
||||
}
|
||||
SetCurrentFrame(m_currentFrame); // re-clamp into the new range
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%.6g fps", m_fps);
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("Auto-Key", &m_autoKey)) NotifyTimeChanged();
|
||||
|
||||
// ---- Frame slider: full width, snaps to whole frames ----
|
||||
float frame = float(m_currentFrame);
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
if (ImGui::SliderFloat("##frame", &frame,
|
||||
float(m_startFrame), float(m_endFrame), "%.0f")) {
|
||||
m_playing = false;
|
||||
SetCurrentFrame(std::round(double(frame)));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void TimelinePanel::SetCurrentFrame(double frame)
|
||||
{
|
||||
frame = std::min(std::max(frame, m_startFrame), m_endFrame);
|
||||
if (frame != m_currentFrame) {
|
||||
m_currentFrame = frame;
|
||||
NotifyTimeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelinePanel::NotifyTimeChanged()
|
||||
{
|
||||
if (OnTimeChanged)
|
||||
OnTimeChanged(pxr::UsdTimeCode(m_currentFrame), EditTime());
|
||||
}
|
||||
|
||||
pxr::UsdTimeCode TimelinePanel::EditTime() const
|
||||
{
|
||||
return m_autoKey ? pxr::UsdTimeCode(m_currentFrame)
|
||||
: pxr::UsdTimeCode::Default();
|
||||
}
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
Reference in New Issue
Block a user