载入中...
搜索中...
未找到
ActionPreviewController.cpp
浏览该文件的文档.
2
3#include <cmath>
4#include <utility>
5
6namespace eve::editor {
7namespace {
8
9Result<void> previewError(EditorStatus status, std::string rule, std::string message) {
10 return eve::editing::failed<void>(status, RuleId(std::move(rule)), std::move(message));
11}
12
13std::string diagnosticMessage(const Status& status, std::string fallback) {
14 if (!status.diagnostics().empty() && !status.diagnostics().front().message().empty())
15 return status.diagnostics().front().message();
16 return fallback;
17}
18
19} // namespace
20
22 const action::IActionRootMotionSource* rootMotion)
23 : editor_(editor), sink_(sink), rootMotion_(rootMotion) {}
24
26 if (sampleCount < 2 || sampleCount > 1024)
27 return previewError(EditorStatus::Rejected, "editor.action.preview.root-samples",
28 "Root-motion sample count must be between 2 and 1024");
29 rootMotionSampleCount_ = sampleCount;
30 return eve::editing::applied<void>();
31}
32
33action::ActionPreviewCueKind ActionPreviewController::cueKind(const action::ActionTimelineEvent& event) noexcept {
36 const std::string_view type = event.type.format();
37 if (type == "presentation:vfx") return action::ActionPreviewCueKind::Vfx;
38 if (type == "presentation:audio") return action::ActionPreviewCueKind::Audio;
39 if (type == "presentation:camera") return action::ActionPreviewCueKind::Camera;
41}
42
43Result<action::ActionPreviewFrame> ActionPreviewController::buildFrame(
45 const std::vector<action::ActionTimelineEvent>& events) const {
47 frame.reason = reason;
48 frame.animationUri = editor_.target().timeline().animationUri;
49 Duration rootMotionDuration = editor_.target().timeline().duration;
50 for (const auto& section : editor_.target().timeline().animationSections) {
51 if (current >= section.start && current < section.end) {
52 frame.animationUri = section.animationUri;
53 rootMotionDuration = Duration::fromNanoseconds(section.end.nanoseconds() - section.start.nanoseconds());
54 break;
55 }
56 }
57 frame.previous = previous;
58 frame.current = current;
59 auto activeBlocks = editor_.target().timeline().activeBlocks(current);
60 if (!activeBlocks) return Result<action::ActionPreviewFrame>::failure(activeBlocks.status());
61 frame.activeBlocks = std::move(activeBlocks).takeValue();
62 frame.cues.reserve(events.size());
63 for (const auto& event : events)
64 frame.cues.push_back({cueKind(event), event.itemId, event.type, event.time, event.payload});
65 if (rootMotion_) {
66 auto path = rootMotion_->sampleRootMotion(frame.animationUri, rootMotionDuration, rootMotionSampleCount_);
69 frame.rootMotionPath = std::move(path).takeValue();
70 }
71 return Result<action::ActionPreviewFrame>::success(std::move(frame));
72}
73
75 auto frame = buildFrame(action::ActionPreviewReason::Refresh, editor_.previewTime(), editor_.previewTime(), {});
76 if (!frame)
77 return previewError(EditorStatus::Rejected, "editor.action.preview.root-motion",
78 diagnosticMessage(frame.status(), "Could not sample root motion"));
79 auto prepared = sink_.prepare(frame.value());
80 if (!prepared)
81 return previewError(EditorStatus::Rejected, "editor.action.preview.prepare",
82 diagnosticMessage(prepared.status(), "Preview host rejected the frame"));
83 sink_.present(frame.value());
84 lastFrame_ = std::move(frame).takeValue();
85 return eve::editing::applied<void>();
86}
87
89 if (time < Duration::zero() || time > editor_.target().timeline().duration)
90 return previewError(EditorStatus::Rejected, "editor.action.preview.seek-range",
91 "Preview seek is outside the timeline");
92 auto frame = buildFrame(action::ActionPreviewReason::Seek, editor_.previewTime(), time, {});
93 if (!frame)
94 return previewError(EditorStatus::Rejected, "editor.action.preview.root-motion",
95 diagnosticMessage(frame.status(), "Could not sample root motion"));
96 auto prepared = sink_.prepare(frame.value());
97 if (!prepared)
98 return previewError(EditorStatus::Rejected, "editor.action.preview.prepare",
99 diagnosticMessage(prepared.status(), "Preview host rejected the frame"));
100 auto changed = editor_.seek(time);
101 if (!changed.ok()) {
102 sink_.discardPrepared();
103 return changed;
104 }
105 sink_.present(frame.value());
106 lastFrame_ = std::move(frame).takeValue();
107 return eve::editing::applied<void>();
108}
109
111 auto stopped = seek(Duration::zero());
112 if (stopped.ok()) editor_.pause();
113 return stopped;
114}
115
116Result<void> ActionPreviewController::stepFrames(std::int64_t frames, double frameRate) {
117 auto target = editor_.frameStepTarget(frames, frameRate);
118 if (!target.ok()) return Result<void>::failure(target.status());
119 auto stepped = seek(target.value());
120 if (stepped.ok()) editor_.pause();
121 return stepped;
122}
123
125 auto jumped = seek(editor_.target().timeline().duration);
126 if (jumped.ok()) editor_.pause();
127 return jumped;
128}
129
131 auto plan = editor_.planPreview(delta);
132 if (!plan.ok())
133 return eve::editing::failed<std::size_t>(plan.code(), RuleId("editor.action.preview.plan"),
134 "Could not prepare timeline preview advance");
135 if (plan.code() == EditorStatus::NoOp)
136 return Result<std::size_t>::success(0, Status::success(EditorStatus::NoOp));
137 auto frame = buildFrame(action::ActionPreviewReason::Advance, plan.value().previous, plan.value().current,
138 plan.value().events);
139 if (!frame)
140 return eve::editing::failed<std::size_t>(EditorStatus::Rejected, RuleId("editor.action.preview.root-motion"),
141 diagnosticMessage(frame.status(), "Could not sample root motion"));
142 auto prepared = sink_.prepare(frame.value());
143 if (!prepared)
144 return eve::editing::failed<std::size_t>(
145 EditorStatus::Rejected, RuleId("editor.action.preview.prepare"),
146 diagnosticMessage(prepared.status(), "Preview host rejected the frame"));
147 auto advanced = editor_.applyPreviewPlan(std::move(plan).takeValue());
148 if (!advanced.ok()) {
149 sink_.discardPrepared();
150 return advanced;
151 }
152 sink_.present(frame.value());
153 lastFrame_ = std::move(frame).takeValue();
154 return advanced;
155}
156
158 if (!lastFrame_ || lastFrame_->rootMotionState != action::RootMotionPreviewState::Available ||
159 !std::isfinite(scale) || scale <= 0.0f)
160 return;
161 for (std::size_t index = 1; index < lastFrame_->rootMotionPath.size(); ++index) {
162 const auto& previous = lastFrame_->rootMotionPath[index - 1];
163 const auto& current = lastFrame_->rootMotionPath[index];
164 overlay.line({origin.x + previous.x * scale, origin.y + previous.y * scale, origin.z + previous.z * scale},
165 {origin.x + current.x * scale, origin.y + current.y * scale, origin.z + current.z * scale},
166 {0x55d6beffU, 2.0f, false});
167 }
168}
169
170} // namespace eve::editor
LogicalId target
Atomic action-preview transport and presentation orchestration.
std::string message
wgpu::PopErrorScopeStatus status
std::array< float, 3 > scale
graphics::Canvas * previous
std::string path
Definition PlayHost.cpp:110
V3 origin
Definition RoadBake.cpp:138
double current
Battle::Events events
uint32_t index
Signed, fixed-resolution simulation duration in nanoseconds.
Definition Time.h:43
static constexpr Duration fromNanoseconds(std::int64_t nanoseconds) noexcept
Construct an exact duration from nanoseconds.
Definition Time.h:55
static constexpr Duration zero() noexcept
Return zero duration.
Definition Time.h:60
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
Host boundary for 3D animation, VFX, audio and camera preview.
virtual Result< void > prepare(const ActionPreviewFrame &frame)=0
Validate and stage every resource required by a frame.
virtual void present(const ActionPreviewFrame &frame) noexcept=0
Publish one successfully prepared frame without failure or callbacks.
virtual void discardPrepared() noexcept=0
Discard staged resources when the authoritative transport commit fails.
Optional animation provider that samples an owning root-motion polyline.
virtual Result< std::vector< ActionPreviewPoint3 > > sampleRootMotion(std::string_view animationUri, Duration duration, std::uint32_t sampleCount) const =0
Sample animation root motion from zero through duration, including both endpoints.
Result< void > jumpToEnd()
Atomically seek to the final frame boundary and pause transport.
Result< void > seek(Duration time)
Atomically prepare presentation and seek the authoritative preview transport.
Result< void > setRootMotionSampleCount(std::uint32_t sampleCount)
Configure root-motion trajectory density in the inclusive range 2..1024.
ActionPreviewController(ActionTimelineEditor &editor, action::IActionPreviewSink &sink, const action::IActionRootMotionSource *rootMotion=nullptr)
Construct with a required presentation sink and optional root-motion provider.
Result< void > stepFrames(std::int64_t frames, double frameRate)
Atomically step signed frames and pause transport at the resulting cursor.
Result< void > refresh()
Re-present the current cursor without changing transport state.
Result< void > stop()
Atomically stop presentation, pause transport and return to the first frame.
void drawRootMotion(IEditorOverlay &overlay, OverlayPoint origin={}, float scale=1.0f) const
Draw the latest root-motion trajectory into a 3D-capable overlay.
Result< std::size_t > update(Duration delta)
Atomically prepare presentation and advance by injected deterministic time.
Timeline editing controller shared by developer and in-game editors.
Duration previewTime() const noexcept
Current deterministic preview cursor.
Result< void > seek(Duration time)
Seek preview without emitting crossed events.
Result< ActionTimelinePreviewPlan > planPreview(Duration delta) const
Prepare a preview advance without mutating transport state.
const ActionTimelineTarget & target() const noexcept
Borrow the authoritative target.
Result< std::size_t > applyPreviewPlan(ActionTimelinePreviewPlan plan)
Commit an unchanged prepared preview advance.
void pause() noexcept
Pause preview playback.
Result< Duration > frameStepTarget(std::int64_t frames, double frameRate) const
Resolve a signed frame step without mutating transport state.
const action::ActionTimeline & timeline() const noexcept
Borrow the authoritative timeline until the next mutation.
Sink implemented by a 2D or 3D viewport to render tool feedback.
virtual void line(const OverlayPoint &from, const OverlayPoint &to, const OverlayStyle &style)=0
Draw a line between two viewport-space points.
ActionPreviewReason
Reason a preview frame is being presented.
ActionPreviewCueKind
Semantic presentation channel selected for one timeline boundary.
eve::editing::RuleId RuleId
Definition EditorIds.h:32
StatusCode
Stable outcome category for an operation.
Definition Status.h:27
Complete frame atomically prepared and then presented by a preview host.
std::vector< ActionActiveBlock > activeBlocks
Owning state-block samples active at current for isolated scene reconciliation.
RootMotionPreviewState rootMotionState
std::vector< ActionPreviewCue > cues
std::vector< ActionPreviewPoint3 > rootMotionPath
Owning event projection returned by the runtime sampler.
Result< std::vector< ActionActiveBlock > > activeBlocks(Duration time) const
Sample every unmuted state active at one authoritative timeline time.
std::string animationUri
Compatibility-only single-clip projection; v2 runtime uses animationSections.
A point in the coordinate space chosen by an editor viewport adapter.