载入中...
搜索中...
未找到
ClimbingExecution.cpp
浏览该文件的文档.
1#include "climbing/Climbing.h"
4#include "physics/Body3D.h"
5#include "physics/World3D.h"
6namespace eve::climbing {
7using namespace runtime_detail;
11
13 const ClimbingMotionInput& motion) {
14 lastQueryCount_ = 0;
15 lastCounters_ = {};
16 lastCounters_.workload = ClimbingWorkload::Active;
18 RuntimeTelemetryScope telemetryScope(telemetry_, lastCounters_, step.tick);
19 if (!isActivePhase(phase_) || !execution_)
21 "no climbing execution is active",
22 "runtime.phase", {}, "climbing"));
23 Execution& execution = *execution_;
24 lastCounters_.selectedCost = execution.candidate.score;
25 if (world.runtimeHandle() != execution.candidate.world)
28 "execution belongs to another or stale physics world", "world", {}, "climbing"));
29 if (step.tick <= execution.lastTick)
31 eve::DiagnosticCode::Conflict, "simulation tick must increase exactly once per update", "step.tick", {},
32 "climbing"));
33 if (step.delta.nanoseconds() <= 0)
35 eve::DiagnosticCode::InvalidArgument, "simulation delta must be positive", "step.delta", {}, "climbing"));
43 eve::DiagnosticCode::InvalidArgument, "motion policies require valid enum values and authored root motion",
44 "motion.policy", {}, "climbing"));
45 if (!isFinite(motion.rootTranslation) || !isFinite(motion.facing) || !isFinite(motion.pelvisOffset) ||
49 "authored motion must be finite and pelvis deviation must remain inside the profile limit", "motion", {},
50 "climbing"));
52 !world.findShape(execution.candidate.obstacleShape))
54 eve::DiagnosticCode::StaleHandle, "traversed shape is stale; collision exclusion was not applied",
55 "motion.obstacleCollision", {}, "climbing"));
56 auto eventCapacity = requireEventCapacity(4, step.tick);
57 if (!eventCapacity) return eve::Result<ClimbingAdvance>::failure(eventCapacity.status());
58
59 const bool graphBound = execution.anchorGraph.isValid() && !execution.anchorReservation.id.isZero();
60 float graphPointSpeed = -1.f;
61 if (graphBound) {
62 auto graph = Climbing::resolveAnchorGraph(execution.anchorGraph);
63 auto resolved = graph.isBound() ? graph->resolveNodeKinematics(world, execution.anchorNode)
65 eve::DiagnosticCode::StaleHandle, "anchor graph instance handle is stale",
66 "execution.anchorGraph", {}, "climbing"));
67 auto reservation = graph.isBound()
68 ? graph->validateReservation(execution.anchorReservation)
70 eve::DiagnosticCode::StaleHandle, "anchor graph instance handle is stale",
71 "execution.anchorGraph", {}, "climbing"));
72 if (!resolved || !reservation) {
73 enqueueEvent(
74 {ClimbingEventKind::Cancelled, execution.candidate.actionId, step.tick, execution.executionId});
76 terminalCode_ = "climbing.anchor.stale";
77 if (graph.isBound() && reservation) {
78 auto released = graph->release(execution.anchorReservation);
79 released.ignore("stale graph anchor resolution releases live occupancy");
80 }
81 execution_.reset();
83 eve::DiagnosticCode::StaleHandle, "graph anchor or occupancy reservation is stale", "execution.anchor",
84 {}, "climbing"));
85 }
86 execution.candidate.obstacleBody = resolved.value().body;
87 execution.candidate.topPoint = resolved.value().position;
88 execution.candidate.frontPoint = resolved.value().position;
89 execution.candidate.surfaceNormal = resolved.value().normal;
90 execution.candidate.surfaceTangent = resolved.value().tangent;
91 execution.candidate.leftHandAnchor = resolved.value().leftHandSocket;
92 execution.candidate.rightHandAnchor = resolved.value().rightHandSocket;
93 if (execution.action.kind == ClimbingActionKind::LadderDismount ||
94 execution.action.kind == ClimbingActionKind::BeamBalance) {
95 execution.candidate.landingFeet = resolved.value().position;
96 } else {
97 execution.candidate.landingFeet =
98 resolved.value().position +
99 resolved.value().normal * (profile_.capsuleRadius + profile_.skin + execution.action.hangBodyOffset);
100 execution.candidate.landingFeet.y = resolved.value().position.y - execution.action.hangFeetBelowLedge;
101 }
102 graphPointSpeed = length(resolved.value().pointVelocity);
103 }
104
105 physics::Body3D* obstacle = world.findBody(execution.candidate.obstacleBody);
106 if (!obstacle) {
107 enqueueEvent({ClimbingEventKind::Cancelled, execution.candidate.actionId, step.tick, execution.executionId});
109 terminalCode_ = "climbing.anchor.stale";
110 if (graphBound) {
111 auto released = releaseAnchorReservation();
112 released.ignore("destroyed anchor body releases graph occupancy");
113 }
114 execution_.reset();
116 eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "climbing target handle is stale",
117 "execution.candidate.obstacleBody", {}, "climbing"));
118 }
119 const float platformSpeed = graphPointSpeed >= 0.f
120 ? graphPointSpeed
121 : std::sqrt(obstacle->getLinearVelocityX() * obstacle->getLinearVelocityX() +
122 obstacle->getLinearVelocityY() * obstacle->getLinearVelocityY() +
123 obstacle->getLinearVelocityZ() * obstacle->getLinearVelocityZ());
124 if (platformSpeed > profile_.maxPlatformSpeed + epsilon) {
125 enqueueEvent({ClimbingEventKind::Cancelled, execution.candidate.actionId, step.tick, execution.executionId});
127 terminalCode_ = "climbing.anchor.platform_speed";
128 if (graphBound) {
129 auto released = releaseAnchorReservation();
130 released.ignore("unsafe anchor platform speed releases graph occupancy");
131 }
132 execution_.reset();
134 eve::DiagnosticCode::PreconditionViolation, "climbing target exceeded the platform speed limit",
135 "execution.candidate.obstacleBody", {}, "climbing"));
136 }
137 if (!graphBound) {
138 auto worldTop = obstacle->localToWorldPointOwned(
139 execution.candidate.bodyLocalTop.x, execution.candidate.bodyLocalTop.y, execution.candidate.bodyLocalTop.z);
140 if (!worldTop) return eve::Result<ClimbingAdvance>::failure(worldTop.status());
141 auto worldLanding = obstacle->localToWorldPointOwned(execution.candidate.bodyLocalLanding.x,
142 execution.candidate.bodyLocalLanding.y,
143 execution.candidate.bodyLocalLanding.z);
144 if (!worldLanding) return eve::Result<ClimbingAdvance>::failure(worldLanding.status());
145 execution.candidate.topPoint = {worldTop.value().x, worldTop.value().y, worldTop.value().z};
146 if (execution.candidate.kind == ClimbingActionKind::ClimbUp) {
147 execution.candidate.landingFeet =
148 execution.candidate.topPoint - execution.candidate.surfaceNormal * execution.action.landingForward;
149 } else {
150 execution.candidate.landingFeet = {worldLanding.value().x, worldLanding.value().y, worldLanding.value().z};
151 }
152 execution.candidate.leftHandAnchor =
153 execution.candidate.topPoint - execution.candidate.surfaceTangent * (execution.action.handSpacing * 0.5f);
154 execution.candidate.rightHandAnchor =
155 execution.candidate.topPoint + execution.candidate.surfaceTangent * (execution.action.handSpacing * 0.5f);
156 }
157
159 filter.ignoredBodyId = execution.candidate.ignoredBodyId;
161 filter.ignoredShapeId = execution.candidate.obstacleShapeId;
162 if (phase_ == ClimbingPhase::Hanging || phase_ == ClimbingPhase::Balanced || phase_ == ClimbingPhase::Swinging) {
163 const float lowerY = execution.currentFeet.y + profile_.capsuleRadius + profile_.skin;
164 const float upperY = execution.currentFeet.y + profile_.capsuleHeight - profile_.capsuleRadius + profile_.skin;
165 auto overlap =
166 world.queryCapsuleOwned(execution.currentFeet.x, lowerY, execution.currentFeet.z, execution.currentFeet.x,
167 upperY, execution.currentFeet.z, profile_.capsuleRadius, filter);
168 if (!overlap) return eve::Result<ClimbingAdvance>::failure(overlap.status());
169 ++lastQueryCount_;
170 lastCounters_.queryCount = lastQueryCount_;
171 if (overlap.value().bodyCount != 0) {
172 enqueueEvent({ClimbingEventKind::Failed, execution.candidate.actionId, step.tick, execution.executionId});
173 phase_ = ClimbingPhase::Failed;
174 terminalCode_ = "climbing.candidate.clearance_blocked";
175 if (graphBound) {
176 auto released = releaseAnchorReservation();
177 released.ignore("blocked hanging clearance releases graph occupancy");
178 }
179 execution_.reset();
181 eve::DiagnosticCode::PreconditionViolation, "hanging capsule clearance became blocked",
182 "execution.clearance", {}, "climbing"));
183 }
184 execution.lastTick = step.tick;
185 ClimbingAdvance output{phase_, execution.candidate.actionId, execution.currentFeet, {}, {}, {}, 1.f, false,
186 false, execution.candidate.support};
187 output.leftHandAnchor = execution.candidate.leftHandAnchor;
188 output.rightHandAnchor = execution.candidate.rightHandAnchor;
189 const bool handHold = phase_ != ClimbingPhase::Balanced;
190 const bool authoredContacts = !execution.action.contactConstraints.empty();
191 output.leftHandWeight = authoredContacts
193 : (handHold ? 1.f : 0.f);
194 output.rightHandWeight = authoredContacts
196 : (handHold ? 1.f : 0.f);
197 output.leftFootWeight = activeContactWeight(execution.action, ClimbingContactTarget::LeftFoot, 1.f);
198 output.rightFootWeight = activeContactWeight(execution.action, ClimbingContactTarget::RightFoot, 1.f);
199 output.pelvisWeight = activeContactWeight(execution.action, ClimbingContactTarget::Pelvis, 1.f);
200 output.contactWeight = std::max({output.leftHandWeight, output.rightHandWeight, output.leftFootWeight,
201 output.rightFootWeight, output.pelvisWeight});
202 output.executionId = execution.executionId;
203 output.compactCollisionActive = execution.compactCollisionActive;
204 output.branchWindowOpen = execution.branchWindowOpen;
205 output.cameraCueProfile = profile_.cameraCueProfile;
206 output.cameraCue = execution.action.cameraCue;
207 output.animationClipId = execution.action.animation.clipId;
208 output.animationGraphNodeId = execution.action.animation.graphNodeId;
209 output.animationMirrored = execution.action.animation.mirrored;
211 }
212
213 if (phase_ == ClimbingPhase::Dropping) {
214 const float dt = static_cast<float>(step.delta.seconds());
215 execution.velocity.x += world.getGravityX() * dt;
216 execution.velocity.y += world.getGravityY() * dt;
217 execution.velocity.z += world.getGravityZ() * dt;
218 const Vec3 desired = execution.velocity * dt;
219 const float lowerY = execution.currentFeet.y + profile_.capsuleRadius;
220 const float upperY = execution.currentFeet.y + profile_.capsuleHeight - profile_.capsuleRadius;
221 auto moved = world.moveCapsuleOwned(execution.currentFeet.x, lowerY, execution.currentFeet.z,
222 execution.currentFeet.x, upperY, execution.currentFeet.z,
223 profile_.capsuleRadius, desired.x, desired.y, desired.z, filter);
224 if (!moved) return eve::Result<ClimbingAdvance>::failure(moved.status());
225 ++lastQueryCount_;
226 lastCounters_.queryCount = lastQueryCount_;
227 const physics::CapsuleMove3D movement = std::move(moved).takeValue();
228 lastCounters_.moverIterations = static_cast<std::uint32_t>(std::max(0, movement.iterations));
229 const Vec3 actual{movement.deltaX, movement.deltaY, movement.deltaZ};
230 lastCounters_.warpResidual = desired - actual;
231 execution.currentFeet = execution.currentFeet + actual;
232 execution.lastTick = step.tick;
234 execution.candidate.actionId,
235 execution.currentFeet,
236 desired,
237 actual,
238 desired - actual,
239 1.f,
240 movement.constrained,
241 movement.grounded,
243 output.executionId = execution.executionId;
244 output.cameraCueProfile = profile_.cameraCueProfile;
245 output.cameraCue = execution.action.cameraCue;
246 output.animationClipId = execution.action.animation.clipId;
247 output.animationGraphNodeId = execution.action.animation.graphNodeId;
248 output.animationMirrored = execution.action.animation.mirrored;
249 if (movement.grounded) {
251 terminalCode_ = "climbing.completed";
252 output.phase = phase_;
253 enqueueEvent({ClimbingEventKind::Landed, output.actionId, step.tick, execution.executionId});
254 enqueueEvent({ClimbingEventKind::Completed, output.actionId, step.tick, execution.executionId});
255 output.terminalVelocity = terminalVelocityFor(execution.action, actual, 1.f / dt);
256 output.hasTerminalVelocity = true;
257 execution_.reset();
258 }
260 }
261
262 auto elapsed = execution.elapsed.tryAdd(step.delta);
263 if (!elapsed) return eve::Result<ClimbingAdvance>::failure(elapsed.status());
264 execution.elapsed = std::move(elapsed).takeValue();
265 execution.lastTick = step.tick;
266 const double durationSeconds = execution.duration.seconds();
267 const float t = static_cast<float>(std::clamp(execution.elapsed.seconds() / durationSeconds, 0.0, 1.0));
268 const Vec3 planned =
269 detail::trajectoryPoint(execution.startFeet, execution.candidate, execution.action, profile_, t);
270 const Vec3 proceduralDelta = planned - execution.currentFeet;
271 Vec3 baseDelta = proceduralDelta;
273 baseDelta = motion.rootTranslation;
274 } else if (motion.hasRootMotion) {
275 const float authoredLength = length(motion.rootTranslation);
276 const float targetLength = length(proceduralDelta);
277 const float scale = authoredLength > epsilon
278 ? std::clamp(targetLength / authoredLength, execution.action.rootMotionScaleMin,
279 execution.action.rootMotionScaleMax)
280 : execution.action.rootMotionScaleMin;
281 baseDelta = motion.rootTranslation * scale;
282 }
283
285 ? WarpChannels{}
286 : activeWarpChannels(execution.action, t);
287 const Vec3 warpError = planned - (execution.currentFeet + baseDelta);
288 Vec3 appliedWarp;
289 if (channels.horizontal) {
290 Vec3 horizontal{warpError.x, 0.f, warpError.z};
291 const float remaining = std::max(0.f, execution.action.horizontalWarpBudget - execution.horizontalWarpUsed);
292 horizontal = clampMagnitude(horizontal, std::min(execution.action.maxTranslationWarpPerTick, remaining));
293 appliedWarp.x = horizontal.x;
294 appliedWarp.z = horizontal.z;
295 }
296 if (channels.vertical) {
297 const float remaining = std::max(0.f, execution.action.verticalWarpBudget - execution.verticalWarpUsed);
298 appliedWarp.y = std::clamp(warpError.y, -std::min(execution.action.maxTranslationWarpPerTick, remaining),
299 std::min(execution.action.maxTranslationWarpPerTick, remaining));
300 }
301 const float remainingTotalWarp =
302 std::max(0.f, profile_.maxTotalWarpBudget - execution.horizontalWarpUsed - execution.verticalWarpUsed);
303 appliedWarp = clampMagnitude(appliedWarp, std::min(execution.action.maxTranslationWarpPerTick, remainingTotalWarp));
304 execution.horizontalWarpUsed += length({appliedWarp.x, 0.f, appliedWarp.z});
305 execution.verticalWarpUsed += std::fabs(appliedWarp.y);
306 const Vec3 desired = baseDelta + appliedWarp;
307
308 float desiredYawDelta = motion.rootYawRadians;
309 if (channels.facing) {
310 const Vec3 targetFacing = execution.candidate.surfaceNormal * -1.f;
311 const float yawError = signedHorizontalAngle(motion.facing, targetFacing) - motion.rootYawRadians;
312 const float remaining = std::max(0.f, execution.action.facingWarpBudgetRadians - execution.facingWarpUsed);
313 const float correction = std::clamp(yawError, -std::min(execution.action.maxYawWarpRadiansPerTick, remaining),
314 std::min(execution.action.maxYawWarpRadiansPerTick, remaining));
315 execution.facingWarpUsed += std::fabs(correction);
316 desiredYawDelta += correction;
317 }
318 const float authoredLeftHand = activeContactWeight(execution.action, ClimbingContactTarget::LeftHand, t);
319 const float authoredRightHand = activeContactWeight(execution.action, ClimbingContactTarget::RightHand, t);
320 bool leftContact = authoredLeftHand > epsilon;
321 bool rightContact = authoredRightHand > epsilon;
322 bool landContact = false;
323 bool compactRequested = false;
324 bool compactForTick = execution.compactCollisionActive;
325 bool branchForTick =
326 execution.action.branchWindows.empty() ? execution.branchWindowOpen : activeBranchWindow(execution.action, t);
327 for (ClimbingNotifyKind notify : motion.notifies) {
328 switch (notify) {
329 case ClimbingNotifyKind::ContactLeftHand: leftContact = true; break;
330 case ClimbingNotifyKind::ContactRightHand: rightContact = true; break;
332 compactRequested = true;
333 compactForTick = true;
334 break;
335 case ClimbingNotifyKind::BranchOpen: branchForTick = true; break;
336 case ClimbingNotifyKind::BranchClose: branchForTick = false; break;
338 landContact = true;
339 compactForTick = false;
340 branchForTick = false;
341 break;
342 }
343 }
344 const float collisionHeight = compactForTick ? profile_.compactCapsuleHeight : profile_.capsuleHeight;
345 const float lowerY = execution.currentFeet.y + profile_.capsuleRadius;
346 const float upperY = execution.currentFeet.y + collisionHeight - profile_.capsuleRadius;
347 auto moved = world.moveCapsuleOwned(execution.currentFeet.x, lowerY, execution.currentFeet.z,
348 execution.currentFeet.x, upperY, execution.currentFeet.z,
349 profile_.capsuleRadius, desired.x, desired.y, desired.z, filter);
350 if (!moved) return eve::Result<ClimbingAdvance>::failure(moved.status());
351 ++lastQueryCount_;
352 lastCounters_.queryCount = lastQueryCount_;
353 const physics::CapsuleMove3D movement = std::move(moved).takeValue();
354 lastCounters_.moverIterations = static_cast<std::uint32_t>(std::max(0, movement.iterations));
355 const Vec3 actual{movement.deltaX, movement.deltaY, movement.deltaZ};
356 execution.currentFeet = execution.currentFeet + actual;
357 execution.lastPlannedFeet = planned;
358 const Vec3 residual = desired - actual;
359 lastCounters_.warpResidual = residual;
360 execution.accumulatedResidual = execution.accumulatedResidual + residual;
361 execution.compactCollisionActive = compactForTick;
362 execution.branchWindowOpen = branchForTick;
363 if (debugCapture_ == ClimbingDebugCapture::Enabled)
364 boundedDebugPush(motionEvidence_, ClimbingMotionEvidence{step.tick, planned, execution.currentFeet, residual,
365 collisionHeight, movement.constrained});
367 execution.candidate.actionId,
368 execution.currentFeet,
369 desired,
370 actual,
371 residual,
372 t,
373 movement.constrained,
374 movement.grounded,
375 execution.candidate.support};
376 output.executionId = execution.executionId;
377 output.appliedWarp = appliedWarp;
378 output.desiredYawDelta = desiredYawDelta;
379 output.cameraCueProfile = profile_.cameraCueProfile;
380 output.cameraCue = execution.action.cameraCue;
381 output.animationClipId = execution.action.animation.clipId;
382 output.animationGraphNodeId = execution.action.animation.graphNodeId;
383 output.animationMirrored = execution.action.animation.mirrored;
384 output.leftHandAnchor = execution.candidate.leftHandAnchor;
385 output.rightHandAnchor = execution.candidate.rightHandAnchor;
386 if (leftContact && !execution.leftContactEmitted) {
387 execution.leftContactEmitted = true;
388 enqueueEvent({ClimbingEventKind::ContactLeftHand, output.actionId, step.tick, execution.executionId});
389 }
390 if (rightContact && !execution.rightContactEmitted) {
391 execution.rightContactEmitted = true;
392 enqueueEvent({ClimbingEventKind::ContactRightHand, output.actionId, step.tick, execution.executionId});
393 }
394 if (landContact) execution.landContactReleased = true;
395 if (execution.action.contactConstraints.empty()) {
396 output.leftHandWeight = execution.leftContactEmitted && !execution.landContactReleased ? 1.f : 0.f;
397 output.rightHandWeight = execution.rightContactEmitted && !execution.landContactReleased ? 1.f : 0.f;
398 } else if (!execution.landContactReleased) {
399 output.leftHandWeight = authoredLeftHand;
400 output.rightHandWeight = authoredRightHand;
401 output.leftFootWeight = activeContactWeight(execution.action, ClimbingContactTarget::LeftFoot, t);
402 output.rightFootWeight = activeContactWeight(execution.action, ClimbingContactTarget::RightFoot, t);
403 output.pelvisWeight = activeContactWeight(execution.action, ClimbingContactTarget::Pelvis, t);
404 }
405 output.contactWeight = std::max({output.leftHandWeight, output.rightHandWeight, output.leftFootWeight,
406 output.rightFootWeight, output.pelvisWeight});
407 output.compactCollisionRequested = compactRequested;
408 output.compactCollisionActive = execution.compactCollisionActive;
409 output.branchWindowOpen = execution.branchWindowOpen;
410 if (const std::string* comboTag = activeBranchComboTag(execution.action, t)) output.branchComboTag = *comboTag;
411 if (t < 0.1f)
413 else if (t < 0.2f)
415 else if (t < 0.8f)
417 else if (t < 0.95f)
418 phase_ = ClimbingPhase::Landing;
419 else
421 output.phase = phase_;
422 const Vec3 remainingTargetError = planned - execution.currentFeet;
423 const bool authoredWarpMissed = motion.hasRootMotion &&
425 t + epsilon >= finalWarpWindowEnd(execution.action) &&
426 length(remainingTargetError) > profile_.maxWarpResidual + epsilon;
427 if (length(execution.accumulatedResidual) > profile_.maxWarpResidual + epsilon || authoredWarpMissed) {
428 phase_ = ClimbingPhase::Failed;
429 terminalCode_ = "climbing.warp.budget_exceeded";
430 output.phase = phase_;
431 enqueueEvent({ClimbingEventKind::Failed, output.actionId, step.tick, execution.executionId});
432 if (graphBound) {
433 auto released = releaseAnchorReservation();
434 released.ignore("failed anchor motion releases graph occupancy");
435 }
436 execution_.reset();
437 } else if (t >= 1.f - epsilon) {
438 if (graphBound && isAnchorHangEnd(execution.candidate.kind)) {
439 if (execution.candidate.kind == ClimbingActionKind::BeamBalance)
441 else if (execution.candidate.kind == ClimbingActionKind::PoleSwing ||
442 execution.candidate.kind == ClimbingActionKind::BarSwing)
444 else
445 phase_ = ClimbingPhase::Hanging;
446 execution.branchWindowOpen = true;
447 output.branchWindowOpen = true;
448 enqueueEvent({execution.candidate.kind == ClimbingActionKind::LedgeGrab ? ClimbingEventKind::Hanging
450 output.actionId, step.tick, execution.executionId});
451 } else if (execution.candidate.kind == ClimbingActionKind::LedgeGrab) {
452 phase_ = ClimbingPhase::Hanging;
453 execution.branchWindowOpen = true;
454 output.branchWindowOpen = true;
455 enqueueEvent({ClimbingEventKind::Hanging, output.actionId, step.tick, execution.executionId});
456 } else {
458 terminalCode_ = "climbing.completed";
459 if (execution.candidate.kind != ClimbingActionKind::WallRun)
460 enqueueEvent({ClimbingEventKind::Landed, output.actionId, step.tick, execution.executionId});
461 enqueueEvent({ClimbingEventKind::Completed, output.actionId, step.tick, execution.executionId});
462 if (graphBound) {
463 auto released = releaseAnchorReservation();
464 released.ignore("completed graph dismount releases occupancy");
465 }
466 }
467 output.phase = phase_;
468 if (phase_ == ClimbingPhase::Completed) {
469 output.terminalVelocity =
470 terminalVelocityFor(execution.action, actual, static_cast<float>(1.0 / step.delta.seconds()));
471 output.hasTerminalVelocity = true;
472 execution_.reset();
473 }
474 }
476}
477
478
479} // namespace eve::climbing
std::map< std::string, std::vector< Key >, std::less<> > channels
std::string output
float length
Definition CaveMesh.cpp:94
Deterministic climbing/parkour planning and capsule-constrained execution.
std::array< float, 3 > scale
std::map< std::string, std::vector< std::string > > graph
Definition Package.cpp:59
World3D * world
float t
std::string filter
float step
Definition TreeMesh.cpp:314
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
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
eve::Result< ClimbingAdvance > advance(physics::World3D &world, eve::SimulationStep step)
Advances the active trajectory through the world's capsule mover.
static eve::script::Borrowed< ClimbingAnchorGraphInstance > resolveAnchorGraph(ClimbingAnchorGraphHandleRef reference) noexcept
Resolves a live graph instance as a synchronous non-owning observation.
3D rigid body (Box3D) in meter-space coordinates (+Y up by convention). Owned by a World3D; create pr...
Definition Body3D.h:24
float getLinearVelocityY() const
Returns the linear velocity y.
Definition Body3D.cpp:321
eve::Result< PhysicsVector3D > localToWorldPointOwned(float x, float y, float z) const
Converts a local point to an allocation-free owning value with structured stale/input failure.
Definition Body3D.cpp:364
float getLinearVelocityZ() const
Returns the linear velocity z.
Definition Body3D.cpp:326
float getLinearVelocityX() const
Returns the linear velocity x.
Definition Body3D.cpp:316
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:63
Vec3 trajectoryPoint(Vec3 start, const ClimbingCandidate &candidate, const ClimbingActionDefinition &action, const ClimbingProfileDefinition &profile, float normalizedTime) noexcept
Trajectory point.
WarpChannels activeWarpChannels(const ClimbingActionDefinition &action, float normalizedTime)
Active warp channels.
float activeContactWeight(const ClimbingActionDefinition &action, ClimbingContactTarget target, float normalizedTime)
Active contact weight.
const std::string * activeBranchComboTag(const ClimbingActionDefinition &action, float normalizedTime)
Optional combo tag. @borrowed From action; lifetime ends when the caller-owned action changes or is d...
float finalWarpWindowEnd(const ClimbingActionDefinition &action)
Final warp window end.
Vec3 clampMagnitude(Vec3 value, float maximum)
Clamp magnitude.
bool isFinite(float value)
True when finite.
void boundedDebugPush(std::vector< T > &values, T value)
Bounded debug push.
bool isActivePhase(ClimbingPhase phase)
True when active phase.
bool activeBranchWindow(const ClimbingActionDefinition &action, float normalizedTime)
Active branch window.
float signedHorizontalAngle(Vec3 from, Vec3 to)
Signed horizontal angle.
Vec3 terminalVelocityFor(const ClimbingActionDefinition &action, Vec3 actualDelta, float inverseDelta)
Terminal velocity for.
bool isAnchorHangEnd(ClimbingActionKind kind)
True when anchor hang end.
ClimbingNotifyKind
Stable animation notify semantics accepted from a validated climbing clip.
Definition Climbing.h:503
One deterministic fixed-step emitted by SimulationClock.
Definition Time.h:158
Owning output of one execution tick.
Definition Climbing.h:553
One planned-versus-actual capsule sample from an authoritative execution tick.
Definition Climbing.h:623
Per-tick authored animation motion supplied before climbing warp and collision.
Definition Climbing.h:540
ClimbingObstacleCollision obstacleCollision
Definition Climbing.h:549
std::vector< ClimbingNotifyKind > notifies
Owning semantic notifies crossed by the animation player during this exact simulation step.
Definition Climbing.h:547
ClimbingRootMotionPolicy rootMotionPolicy
Definition Climbing.h:548
physics::QueryFilter3D queryFilter
Definition Climbing.h:254
static constexpr std::uint32_t Active
Small owning world-space vector used at climbing module boundaries.
Owning result of continuous capsule movement.
Query filter applied atomically for one owning query operation.