载入中...
搜索中...
未找到
MontageCoordinator.cpp
浏览该文件的文档.
2
6
7#include <algorithm>
8#include <cmath>
9#include <limits>
10#include <utility>
11
12namespace eve::animation {
13namespace {
14
15} // namespace
16
18 : skeleton_(skeleton), composedPose_(std::make_unique<AnimPose>(skeleton.getBoneCount())) {
19 skeleton_.applyBindPose(composedPose_.get());
20}
21
22void MontageCoordinator::ensureLayer(std::size_t layer) {
23 while (layers_.size() <= layer) {
24 Layer entry;
25 entry.pose = std::make_unique<AnimPose>(skeleton_.getBoneCount());
26 entry.rawPose = std::make_unique<AnimPose>(skeleton_.getBoneCount());
27 entry.blendScratch = std::make_unique<AnimPose>(skeleton_.getBoneCount());
28 skeleton_.applyBindPose(entry.pose.get());
29 skeleton_.applyBindPose(entry.rawPose.get());
30 skeleton_.applyBindPose(entry.blendScratch.get());
31 layers_.push_back(std::move(entry));
32 }
33}
34
35void MontageCoordinator::retireSlot(Slot& slot) noexcept {
36 slot.player.reset();
37 auto next = MontageHandle::nextGeneration(slot.generation);
38 if (next)
39 slot.generation = *next;
40 else
41 slot.retired = true;
42}
43
45 std::vector<MontageClipAsset>&& clips,
47 if (layer > static_cast<std::size_t>(std::numeric_limits<MontageHandle::index_type>::max() / 2U))
49 Diagnostic::error(DiagnosticCode::InvalidArgument, "montage layer is too large", "layer"));
50 ensureLayer(layer);
51 Layer& entry = layers_[layer];
52 const std::size_t nextIndex = 1U - entry.active;
53 Slot& next = entry.slots[nextIndex];
54 if (next.retired)
56 Diagnostic::error(DiagnosticCode::Failed, "montage slot generation is exhausted", "slot"));
57 if (next.player) retireSlot(next);
58 if (next.retired)
60 Diagnostic::error(DiagnosticCode::Failed, "montage slot generation is exhausted", "slot"));
61
62 auto candidate = std::make_unique<MontagePlayer>(skeleton_);
63 if (entry.rootMotionReceiver) candidate->setRootMotionReceiver(*entry.rootMotionReceiver);
64 if (entry.rootMotionPolicy) {
65 auto applied = candidate->setRootMotionPolicy(*entry.rootMotionPolicy);
66 if (!applied) return Result<MontageHandle>::failure(applied.status());
67 }
68 const Duration blendOut = timeline.montage.defaultBlendIn;
69 auto prepared = candidate->prepare(std::move(timeline), std::move(clips));
70 if (!prepared) return Result<MontageHandle>::failure(prepared.status());
71 auto started = candidate->play(executionId);
72 if (!started) return Result<MontageHandle>::failure(started.status());
73 Slot& previous = entry.slots[entry.active];
74 if (previous.player && (previous.player->isPlaying() || previous.player->isFinished())) {
75 auto fading = previous.player->beginBlendOut(blendOut, tick);
76 if (!fading) return Result<MontageHandle>::failure(fading.status());
77 }
78 next.player = std::move(candidate);
79 entry.active = nextIndex;
81 MontageHandle(static_cast<MontageHandle::index_type>(handleIndex(layer, nextIndex)), next.generation),
83}
84
85MontageCoordinator::Slot* MontageCoordinator::slotFor(MontageHandle handle) noexcept {
86 if (!handle.isValid()) return nullptr;
87 const std::size_t layer = handle.index() / 2U;
88 const std::size_t slot = handle.index() % 2U;
89 if (layer >= layers_.size()) return nullptr;
90 Slot& candidate = layers_[layer].slots[slot];
91 return candidate.player && candidate.generation == handle.generation() ? &candidate : nullptr;
92}
93
94const MontageCoordinator::Slot* MontageCoordinator::slotFor(MontageHandle handle) const noexcept {
95 return const_cast<MontageCoordinator*>(this)->slotFor(handle);
96}
97
99 Slot* slot = slotFor(handle);
100 if (!slot)
102 Diagnostic::error(DiagnosticCode::StaleHandle, "montage handle is invalid or stale", "handle"));
103 return Result<std::reference_wrapper<MontagePlayer>>::success(std::ref(*slot->player));
104}
105
107 const Slot* slot = slotFor(handle);
108 if (!slot)
110 Diagnostic::error(DiagnosticCode::StaleHandle, "montage handle is invalid or stale", "handle"));
111 return Result<std::reference_wrapper<const MontagePlayer>>::success(std::cref(*slot->player));
112}
113
116 auto player = resolve(handle);
117 if (!player) return Result<MontageAdvance>::failure(player.status());
118 return player.value().get().present(advance, tick);
119}
120
122 for (Layer& layer : layers_) {
123 for (Slot& slot : layer.slots) {
124 if (!slot.player || !slot.player->isBlendingOut()) continue;
125 const MontageHandle handle(static_cast<MontageHandle::index_type>(&layer - layers_.data()) * 2U +
126 static_cast<MontageHandle::index_type>(&slot - layer.slots),
127 slot.generation);
128 if (handle == except) continue;
129 auto advanced = slot.player->advanceBlendOut(delta, tick);
130 if (!advanced) return Result<void>::failure(advanced.status());
131 }
132 }
135}
136
138 auto player = resolve(handle);
139 if (!player) return Result<MontageAdvance>::failure(player.status());
140 return player.value().get().beginBlendOut(blendOut, tick);
141}
142
144 if (layer > static_cast<std::size_t>(std::numeric_limits<MontageHandle::index_type>::max() / 2U))
146 Diagnostic::error(DiagnosticCode::InvalidArgument, "montage layer is too large", "layer"));
147 if (weights.size() != static_cast<std::size_t>(skeleton_.getBoneCount()))
149 "montage layer mask must contain one weight per skeleton bone",
150 "weights"));
151 if (std::any_of(weights.begin(), weights.end(), [](float weight) {
152 return !std::isfinite(weight) || weight < 0.0f || weight > 1.0f;
153 }))
155 DiagnosticCode::InvalidArgument, "montage layer mask weights must be finite and within [0,1]", "weights"));
156 ensureLayer(layer);
157 layers_[layer].boneMask = std::move(weights);
159}
160
162 if (mask.getSkeleton() != &skeleton_)
164 "montage layer mask belongs to a different skeleton", "mask"));
165 std::vector<float> weights;
166 weights.reserve(static_cast<std::size_t>(mask.getBoneCount()));
167 for (int bone = 0; bone < mask.getBoneCount(); ++bone) weights.push_back(mask.getBoneWeight(bone));
168 return setLayerBoneMask(layer, std::move(weights));
169}
170
172 if (layer >= layers_.size())
174 Diagnostic::error(DiagnosticCode::NotFound, "montage layer does not exist", "layer"));
175 if (layers_[layer].boneMask.empty()) return Result<void>::success(Status::success(StatusCode::NoOp));
176 layers_[layer].boneMask.clear();
178}
179
181 if (layer >= layers_.size())
182 return Result<std::vector<float>>::failure(
183 Diagnostic::error(DiagnosticCode::NotFound, "montage layer does not exist", "layer"));
184 return Result<std::vector<float>>::success(layers_[layer].boneMask);
185}
186
188 if (layer > static_cast<std::size_t>(std::numeric_limits<MontageHandle::index_type>::max() / 2U))
190 Diagnostic::error(DiagnosticCode::InvalidArgument, "montage layer is too large", "layer"));
191 if (!std::isfinite(weight) || weight < 0.0f || weight > 1.0f)
193 DiagnosticCode::InvalidArgument, "montage layer weight must be finite and within [0,1]", "weight"));
194 ensureLayer(layer);
196 layers_[layer].weight = weight;
198}
199
201 if (layer >= layers_.size())
203 Diagnostic::error(DiagnosticCode::NotFound, "montage layer does not exist", "layer"));
204 return Result<float>::success(layers_[layer].weight);
205}
206
208 if (layer > static_cast<std::size_t>(std::numeric_limits<MontageHandle::index_type>::max() / 2U))
210 Diagnostic::error(DiagnosticCode::InvalidArgument, "montage layer is too large", "layer"));
211 ensureLayer(layer);
212 if (layers_[layer].additive == additive) return Result<void>::success(Status::success(StatusCode::NoOp));
213 layers_[layer].additive = additive;
215}
216
218 if (layer >= layers_.size())
220 Diagnostic::error(DiagnosticCode::NotFound, "montage layer does not exist", "layer"));
221 return Result<bool>::success(layers_[layer].additive);
222}
223
225 IMontageRootMotionReceiver& receiver) {
226 if (layer > static_cast<std::size_t>(std::numeric_limits<MontageHandle::index_type>::max() / 2U))
228 Diagnostic::error(DiagnosticCode::InvalidArgument, "montage layer is too large", "layer"));
229 ensureLayer(layer);
230 Layer& entry = layers_[layer];
231 entry.rootMotionReceiver = &receiver;
232 for (Slot& slot : entry.slots)
233 if (slot.player) slot.player->setRootMotionReceiver(receiver);
235}
236
238 if (layer >= layers_.size())
240 Diagnostic::error(DiagnosticCode::NotFound, "montage layer does not exist", "layer"));
241 Layer& entry = layers_[layer];
242 if (!entry.rootMotionReceiver) return Result<void>::success(Status::success(StatusCode::NoOp));
243 for (Slot& slot : entry.slots)
244 if (slot.player) slot.player->clearRootMotionReceiver();
245 entry.rootMotionReceiver = nullptr;
247}
248
250 if (layer > static_cast<std::size_t>(std::numeric_limits<MontageHandle::index_type>::max() / 2U))
252 Diagnostic::error(DiagnosticCode::InvalidArgument, "montage layer is too large", "layer"));
253 auto validated = validateRootMotionPolicy(policy);
254 if (!validated) return validated;
255 ensureLayer(layer);
256 Layer& entry = layers_[layer];
257 for (Slot& slot : entry.slots) {
258 if (!slot.player) continue;
259 auto applied = slot.player->setRootMotionPolicy(policy);
260 if (!applied) return applied;
261 }
262 entry.rootMotionPolicy = policy;
264}
265
267 if (layer >= layers_.size())
269 Diagnostic::error(DiagnosticCode::NotFound, "montage layer does not exist", "layer"));
270 Layer& entry = layers_[layer];
271 if (!entry.rootMotionPolicy) return Result<void>::success(Status::success(StatusCode::NoOp));
272 entry.rootMotionPolicy.reset();
274}
275
276double MontageCoordinator::evaluateLayerPose(Layer& layer) const {
277 const Slot* first = nullptr;
278 const Slot* second = nullptr;
279 double total = 0.0;
280 for (const Slot& slot : layer.slots) {
281 if (!slot.player || slot.player->weight() <= 0.0) continue;
282 total += slot.player->weight();
283 if (!first) first = &slot;
284 else second = &slot;
285 }
286 if (!first) {
287 skeleton_.applyBindPose(layer.rawPose.get());
288 return 0.0;
289 }
290
291 layer.rawPose->copyFrom(&first->player->pose());
292 if (second) {
293 if (total > 0.0) {
294 layer.blendScratch->copyFrom(layer.rawPose.get());
295 layer.rawPose->blendFrom(layer.blendScratch.get(), &second->player->pose(),
296 static_cast<float>(second->player->weight() / total));
297 }
298 }
299 return total;
300}
301
303 if (layerIndex >= layers_.size())
305 Diagnostic::error(DiagnosticCode::NotFound, "montage layer does not exist", "layer"));
306 Layer& layer = layers_[layerIndex];
307 (void)evaluateLayerPose(layer);
308 skeleton_.applyBindPose(layer.pose.get());
309 if (layer.boneMask.empty()) {
310 layer.pose->copyFrom(layer.rawPose.get());
311 } else {
312 for (int bone = 0; bone < layer.pose->getBoneCount(); ++bone) {
313 const float weight = layer.boneMask[static_cast<std::size_t>(bone)];
314 layer.pose->local(bone) = blendTRS(layer.pose->local(bone), layer.rawPose->local(bone), weight);
315 }
316 }
317 return Result<std::reference_wrapper<AnimPose>>::success(std::ref(*layer.pose));
318}
319
321 if (basePose.getBoneCount() != skeleton_.getBoneCount())
323 DiagnosticCode::InvalidArgument, "montage base pose must match the coordinator skeleton", "basePose"));
324 composedPose_->copyFrom(&basePose);
325 for (Layer& layer : layers_) {
326 const double rawWeight = evaluateLayerPose(layer);
327 const float layerWeight = clampf(static_cast<float>(rawWeight), 0.0f, 1.0f) * layer.weight;
328 if (layerWeight <= 0.0f) continue;
329 for (int bone = 0; bone < composedPose_->getBoneCount(); ++bone) {
330 const float maskWeight = layer.boneMask.empty() ? 1.0f : layer.boneMask[static_cast<std::size_t>(bone)];
331 const float weight = clampf(layerWeight * maskWeight, 0.0f, 1.0f);
332 if (weight <= 0.0f) continue;
333 if (layer.additive) {
334 applyAdditiveTRS(composedPose_->local(bone), layer.rawPose->local(bone), skeleton_.bindLocal(bone),
335 weight);
336 } else {
337 composedPose_->local(bone) = blendTRS(composedPose_->local(bone), layer.rawPose->local(bone), weight);
338 }
339 }
340 }
341 composedPose_->computeWorld(&skeleton_);
342 return Result<std::reference_wrapper<AnimPose>>::success(std::ref(*composedPose_));
343}
344
346 for (Layer& layer : layers_)
347 for (Slot& slot : layer.slots)
348 if (slot.player && !slot.player->isPlaying() && !slot.player->isBlendingOut() &&
349 slot.player->weight() <= 0.0)
350 retireSlot(slot);
351}
352
353} // namespace eve::animation
eve::action::ActionExecutionId executionId
int mask
std::int32_t second
std::int32_t first
std::uint32_t bone
Layered dual-slot montage ownership and stable handles.
graphics::Canvas * previous
TileLayer * layer
PrimitiveHandle handle
LocalPageCacheEntry slots[ShadowConfig::kLocalSlots]
SimulationTick tick
float weights[3]
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Signed, fixed-resolution simulation duration in nanoseconds.
Definition Time.h:43
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
Process-local slot identity tagged by the registry that owns it.
constexpr std::optional< RuntimeHandle > nextGeneration() const noexcept
Returns this slot at its next generation.
detail::HandleCoordinates::index_type index_type
Unsigned owner-local slot index type.
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Per-bone weights used by override and additive animation layers.
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:17
int getBoneCount() const
Returns the bone count.
Definition AnimPose.h:36
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
int getBoneCount() const
Returns the bone count.
const TransformTRS & bindLocal(int boneIndex) const
Binds local.
void applyBindPose(class AnimPose *pose) const
Fill pose locals with bind pose.
Optional receiver for filtered per-step root motion.
Result< void > clearLayerRootMotionPolicy(std::size_t layer)
Clear a layer-owned root-motion policy so future slots use MontagePlayer defaults.
Result< void > clearLayerRootMotionReceiver(std::size_t layer)
Clear the borrowed root-motion receiver from current and future slots of a layer.
Result< void > setLayerAdditive(std::size_t layer, bool additive)
Select bind-pose-relative additive or ordered override composition for one layer.
Result< std::reference_wrapper< MontagePlayer > > resolve(MontageHandle handle)
Resolve a live handle for immediate synchronous use.
void collectFinished() noexcept
Reclaim finished slots and invalidate their handles.
Result< std::vector< float > > layerBoneMask(std::size_t layer) const
Return an owning copy of one configured layer mask.
Result< void > setLayerBoneMask(std::size_t layer, std::vector< float > weights)
Atomically replace one layer's owning per-bone blend weights.
Result< void > setLayerRootMotionReceiver(std::size_t layer, IMontageRootMotionReceiver &receiver)
Install one root-motion receiver shared by current and future slots of a layer.
Result< MontageHandle > play(std::size_t layer, action::ActionTimeline timeline, std::vector< MontageClipAsset > &&clips, action::ActionExecutionId executionId, SimulationTick tick)
Prepare and play a montage in the opposite slot of a layer.
Result< void > clearLayerBoneMask(std::size_t layer)
Remove one layer's bone mask so subsequent composition affects every bone.
Result< float > layerWeight(std::size_t layer) const
Return one configured layer's global influence, or NotFound for an absent layer.
MontageCoordinator(AnimSkeleton &skeleton)
Construct over a skeleton that must outlive this coordinator.
Result< MontageAdvance > stop(MontageHandle handle, Duration blendOut, SimulationTick tick)
Stop one live slot with a continuous blend-out.
Result< void > setLayerRootMotionPolicy(std::size_t layer, const RootMotionPolicy &policy)
Install a root-motion policy for current and future slots of a layer.
Result< std::reference_wrapper< AnimPose > > compose(const AnimPose &basePose)
Compose every configured montage layer over an immediate borrowed base pose.
Result< void > setLayerWeight(std::size_t layer, float weight)
Set one layer's finite global influence.
Result< std::reference_wrapper< AnimPose > > pose(std::size_t layer)
Borrow the normalized full layer pose; invalid layers return a structured failure.
Result< void > advanceBlendOuts(Duration delta, SimulationTick tick, MontageHandle except=MontageHandle::invalid())
Advance every fading slot except an optional slot already advanced by its owner.
Result< bool > layerAdditive(std::size_t layer) const
Return whether one configured layer uses additive composition, or NotFound for an absent layer.
Result< MontageAdvance > present(MontageHandle handle, const action::ActionAdvance &advance, SimulationTick tick)
Present one authoritative action advance through a live slot.
TransformTRS blendTRS(const TransformTRS &a, const TransformTRS &b, float t)
Blend trs.
Definition AnimMath.h:79
eve::Result< void > validateRootMotionPolicy(const RootMotionPolicy &policy)
Validate policy fields.
void applyAdditiveTRS(TransformTRS &base, const TransformTRS &sample, const TransformTRS &reference, float weight)
Apply an additive sample onto base using reference.
Definition AnimMath.h:114
RuntimeHandle< MontageHandleTag > MontageHandle
Generation-qualified identity owned by one MontageCoordinator.
float clampf(float v, float lo, float hi)
Clampf.
Definition AnimMath.h:34
Result payload for one deterministic advance call.
Definition Action.h:175
Canonical action timeline asset shared by runtime and editor.
ActionMontageSettings montage
Policy applied after raw root-bone delta extraction. @ownership Value type; owned by AnimPlayer or th...
bool started
Definition Graphics.cpp:183