载入中...
搜索中...
未找到
ClimbingRuntimeInternal.h
浏览该文件的文档.
1#pragma once
2#include <algorithm>
3#include <charconv>
4#include <chrono>
5#include <cmath>
6#include <limits>
7#include <optional>
8#include <utility>
9#include "climbing/Climbing.h"
11
12
13constexpr float epsilon = 1e-5f;
14constexpr std::size_t maxDebugEntries = 64;
15
18public:
23 : buffer_(buffer), counters_(counters), tick_(tick), start_(std::chrono::steady_clock::now()) {}
24
27 const auto elapsed = std::chrono::steady_clock::now() - start_;
28 buffer_.record(
29 {tick_, static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count()),
30 counters_});
31 }
32
33private:
35 ClimbingRuntimeCounters& counters_;
37 std::chrono::steady_clock::time_point start_;
38};
39
40template <class T>
42void boundedDebugPush(std::vector<T>& values, T value) {
43 if (values.size() < maxDebugEntries) values.push_back(std::move(value));
44}
45
47inline bool isFinite(float value) { return std::isfinite(value); }
48
50inline bool isFinite(Vec3 value) { return isFinite(value.x) && isFinite(value.y) && isFinite(value.z); }
51
53inline float lengthSquared(Vec3 value) { return value.x * value.x + value.y * value.y + value.z * value.z; }
55inline float length(Vec3 value) { return std::sqrt(lengthSquared(value)); }
56
58inline Vec3 operator+(Vec3 lhs, Vec3 rhs) { return {lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z}; }
60inline Vec3 operator-(Vec3 lhs, Vec3 rhs) { return {lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z}; }
62inline Vec3 operator*(Vec3 value, float scale) { return {value.x * scale, value.y * scale, value.z * scale}; }
63
66 value.y = 0.f;
67 const float length = std::sqrt(lengthSquared(value));
68 return length > epsilon ? value * (1.f / length) : Vec3{};
69}
70
73 const float magnitude = length(value);
74 return magnitude > maximum && magnitude > epsilon ? value * (maximum / magnitude) : value;
75}
76
79 bool horizontal = false;
80 bool vertical = false;
81 bool facing = false;
82};
83
85inline WarpChannels activeWarpChannels(const ClimbingActionDefinition& action, float normalizedTime) {
86 if (action.warpWindows.empty()) return {true, true, true};
87 for (const ClimbingWarpWindow& window : action.warpWindows)
88 if (normalizedTime + epsilon >= window.start && normalizedTime <= window.end + epsilon)
89 return {window.horizontal, window.vertical, window.facing};
90 return {};
91}
92
94inline bool activeBranchWindow(const ClimbingActionDefinition& action, float normalizedTime) {
96 return std::any_of(action.branchWindows.begin(), action.branchWindows.end(), [&](const auto& window) {
97 return normalizedTime + epsilon >= window.start && normalizedTime <= window.end + epsilon;
98 });
99}
100
103inline const std::string* activeBranchComboTag(const ClimbingActionDefinition& action, float normalizedTime) {
104 const auto found = std::find_if(action.branchWindows.begin(), action.branchWindows.end(), [&](const auto& window) {
105 return normalizedTime + epsilon >= window.start && normalizedTime <= window.end + epsilon;
106 });
107 return found == action.branchWindows.end() ? nullptr : &found->comboTag;
108}
109
112 float normalizedTime) {
113 float weight = 0.f;
114 for (const ClimbingContactConstraint& constraint : action.contactConstraints) {
115 if (constraint.target == target && normalizedTime + epsilon >= constraint.start &&
116 normalizedTime <= constraint.end + epsilon)
117 weight = std::max(weight, constraint.maxWeight);
118 }
119 return weight;
120}
121
123inline Vec3 terminalVelocityFor(const ClimbingActionDefinition& action, Vec3 actualDelta, float inverseDelta) {
124 Vec3 velocity = actualDelta * inverseDelta;
125 switch (action.landingPolicy) {
127 case ClimbingLandingPolicy::MatchGround: velocity.y = 0.f; break;
128 case ClimbingLandingPolicy::Stop: velocity = {}; break;
129 }
130 switch (action.terminalVelocityPolicy) {
132 case ClimbingTerminalVelocityPolicy::ClampDownward: velocity.y = std::min(velocity.y, 0.f); break;
133 case ClimbingTerminalVelocityPolicy::Zero: velocity.y = 0.f; break;
134 }
135 return velocity;
136}
137
139inline float finalWarpWindowEnd(const ClimbingActionDefinition& action) {
140 return action.warpWindows.empty() ? 1.f : action.warpWindows.back().end;
141}
142
147 if (lengthSquared(from) <= epsilon || lengthSquared(to) <= epsilon) return 0.f;
148 const float crossY = from.z * to.x - from.x * to.z;
149 const float dot = std::clamp(from.x * to.x + from.z * to.z, -1.f, 1.f);
151 return std::atan2(crossY, dot);
152}
153
159
166
172
182
184inline std::int64_t quantizeMillimeters(float value) {
185 const double scaled = std::round(static_cast<double>(value) * 1000.0);
186 return static_cast<std::int64_t>(std::clamp(scaled, static_cast<double>(std::numeric_limits<std::int32_t>::min()),
187 static_cast<double>(std::numeric_limits<std::int32_t>::max())));
188}
189
192inline const ClimbingActionDefinition* findAction(const ClimbingProfile& profile, std::string_view id) {
193 const auto found = std::find_if(profile.actions.begin(), profile.actions.end(),
194 [id](const auto& action) { return action.id == id; });
195 return found == profile.actions.end() ? nullptr : &*found;
196}
197
199inline bool isCandidateLess(const ClimbingCandidate& lhs, const ClimbingCandidate& rhs) {
200 if (lhs.score != rhs.score) return lhs.score < rhs.score;
201 if (lhs.actionId != rhs.actionId) return lhs.actionId < rhs.actionId;
202 if (lhs.obstacleBodyId != rhs.obstacleBodyId) return lhs.obstacleBodyId < rhs.obstacleBodyId;
203 return lhs.obstacleShapeId < rhs.obstacleShapeId;
204}
205
207inline bool containsAnyTag(const std::vector<std::string>& actionTags, const std::vector<std::string>& policyTags) {
209 return std::any_of(actionTags.begin(), actionTags.end(), [&](const std::string& tag) {
211 return std::find(policyTags.begin(), policyTags.end(), tag) != policyTags.end();
212 });
213}
214
216inline bool isActionEnabledForPose(const ClimbingProfile& profile, const ClimbingActionDefinition& action,
217 const ClimbingPose& pose) {
218 if (!profile.defaultActionIds.empty() && std::find(profile.defaultActionIds.begin(), profile.defaultActionIds.end(),
219 action.id) == profile.defaultActionIds.end())
220 return false;
221 if (!profile.allowedActionTags.empty() && !containsAnyTag(action.tags, profile.allowedActionTags)) return false;
222 if (containsAnyTag(action.tags, profile.deniedActionTags)) return false;
224 return (static_cast<std::uint8_t>(action.sourceModes) & static_cast<std::uint8_t>(required)) != 0;
225}
226
241
243inline std::optional<int> parseTagSelector(std::string_view selector, std::string_view prefix) {
244 if (!selector.starts_with(prefix)) return std::nullopt;
245 const std::string_view digits = selector.substr(prefix.size());
246 if (digits.empty()) return std::nullopt;
247 int value = 0;
248 const auto parsed = std::from_chars(digits.data(), digits.data() + digits.size(), value);
249 if (parsed.ec != std::errc{} || parsed.ptr != digits.data() + digits.size()) return std::nullopt;
250 return value;
251}
252
254inline bool isSupportSelectorMatch(const ClimbingActionDefinition& action, int shapeTag, int materialId) {
255 for (const std::string& selector : action.requiredSupportTags) {
256 if (const auto expected = parseTagSelector(selector, "shape:")) {
257 if (shapeTag != *expected) return false;
258 } else if (const auto expected = parseTagSelector(selector, "material:")) {
259 if (materialId != *expected) return false;
260 } else {
261 return false;
262 }
263 }
264 return true;
265}
266
268inline std::int64_t weightedMillimeters(float value, std::int32_t weight) {
270 return quantizeMillimeters(value) * static_cast<std::int64_t>(weight);
271}
272
274inline std::int64_t selectionCost(const ClimbingProfile& profile, const ClimbingActionDefinition& action,
275 const ClimbingPose& pose, Vec3 targetDirection, Vec3 targetDelta, float heightError,
276 float distance, std::int64_t stableTieBreak = 0) {
277 const Vec3 forward = normalizedHorizontal(pose.forward);
278 targetDirection = normalizedHorizontal(targetDirection);
279 if (lengthSquared(targetDirection) <= epsilon) targetDirection = forward;
280
281 Vec3 intentDirection =
283 normalizedHorizontal(pose.inputMode == ClimbingInputMode::Precision ? pose.lookIntent : pose.moveIntent);
284 if (lengthSquared(intentDirection) <= epsilon) intentDirection = forward;
285
286 const float directionDot = std::clamp(forward.x * targetDirection.x + forward.z * targetDirection.z, -1.f, 1.f);
287 const float intentDot =
289 std::clamp(intentDirection.x * targetDirection.x + intentDirection.z * targetDirection.z, -1.f, 1.f);
290 const bool precision = pose.inputMode == ClimbingInputMode::Precision;
291 const float assistScale = 1.f - profile.autoAssistStrength * (precision ? 0.15f : 0.5f);
292 const float directionModeScale = precision ? 1.5f : 0.75f;
293 const float speedModeScale = precision ? 0.5f : 1.5f;
294 const float translationModeScale = precision ? 1.25f : 0.75f;
295 const float rotationModeScale = precision ? 1.5f : 0.75f;
296 const float intentModeScale = precision ? 1.5f : 0.5f;
297 const float horizontalDelta = std::sqrt(targetDelta.x * targetDelta.x + targetDelta.z * targetDelta.z);
298 const float warpTranslation = std::sqrt(horizontalDelta * horizontalDelta + targetDelta.y * targetDelta.y);
299 const float warpRotation = std::acos(directionDot);
300 const float intentMismatch = 1.f - intentDot;
301
302 return static_cast<std::int64_t>(action.selectionBias) * 1000000ll +
304 weightedMillimeters((1.f - directionDot) * assistScale * directionModeScale,
305 profile.scoreWeights.direction) +
307 weightedMillimeters(std::fabs(pose.speed - action.minSpeed) * speedModeScale,
308 profile.scoreWeights.approachSpeed) +
310 weightedMillimeters(heightError, profile.scoreWeights.height) +
314 weightedMillimeters(warpTranslation * translationModeScale, profile.scoreWeights.warpTranslation) +
316 weightedMillimeters(warpRotation * rotationModeScale, profile.scoreWeights.warpRotation) +
318 weightedMillimeters(intentMismatch * intentModeScale, profile.scoreWeights.intentMismatch) + stableTieBreak;
319}
320
321
322} // namespace eve::climbing::runtime_detail
LogicalId target
double value
std::string from
eve::EntitySpatialPose pose
float phase
Definition CaveMesh.cpp:58
float length
Definition CaveMesh.cpp:94
Deterministic climbing/parkour planning and capsule-constrained execution.
std::map< std::string, Var > values
float maximum[3]
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
TokenKind kind
std::array< float, 3 > scale
bool required
float distance
const std::string * tag
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
std::string action
Definition PlayHost.cpp:117
bool found
SimulationTick tick
Runtime-owned fixed ring of performance samples.
void record(ClimbingTelemetrySample sample) noexcept
Append one sample, overwriting the oldest sample when full.
RuntimeTelemetryScope(ClimbingTelemetryBuffer &buffer, ClimbingRuntimeCounters &counters, eve::SimulationTick tick) noexcept
Constructs a RuntimeTelemetryScope.
~RuntimeTelemetryScope()
Releases RuntimeTelemetryScope resources.
bool isActionEnabledForPose(const ClimbingProfile &profile, const ClimbingActionDefinition &action, const ClimbingPose &pose)
True when action enabled for pose.
std::int64_t selectionCost(const ClimbingProfile &profile, const ClimbingActionDefinition &action, const ClimbingPose &pose, Vec3 targetDirection, Vec3 targetDelta, float heightError, float distance, std::int64_t stableTieBreak=0)
Selection cost.
Vec3 operator-(Vec3 lhs, Vec3 rhs)
Operator -.
bool isCandidateLess(const ClimbingCandidate &lhs, const ClimbingCandidate &rhs)
True when candidate less.
bool isRuntimeProbeKind(ClimbingActionKind kind)
True when runtime probe kind.
const ClimbingActionDefinition * findAction(const ClimbingProfile &profile, std::string_view id)
Optional action. @borrowed From profile; lifetime ends when the caller-owned profile changes or is de...
std::int64_t quantizeMillimeters(float value)
Quantize millimeters.
Vec3 normalizedHorizontal(Vec3 value)
Normalized horizontal.
WarpChannels activeWarpChannels(const ClimbingActionDefinition &action, float normalizedTime)
Active warp channels.
float activeContactWeight(const ClimbingActionDefinition &action, ClimbingContactTarget target, float normalizedTime)
Active contact weight.
bool isProbeRecipeMatchingKind(const ClimbingActionDefinition &action)
True when probe recipe matching kind.
Vec3 operator+(Vec3 lhs, Vec3 rhs)
Operator +.
float lengthSquared(Vec3 value)
Length squared.
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.
bool isSupportSelectorMatch(const ClimbingActionDefinition &action, int shapeTag, int materialId)
True when support selector match.
Vec3 clampMagnitude(Vec3 value, float maximum)
Clamp magnitude.
bool isFinite(float value)
True when finite.
bool containsAnyTag(const std::vector< std::string > &actionTags, const std::vector< std::string > &policyTags)
True if any tag.
Vec3 operator*(Vec3 value, float scale)
Operator *.
std::int64_t weightedMillimeters(float value, std::int32_t weight)
Weighted millimeters.
void boundedDebugPush(std::vector< T > &values, T value)
Bounded debug push.
bool isObstacleProbeKind(ClimbingActionKind kind)
True when obstacle probe kind.
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.
std::optional< int > parseTagSelector(std::string_view selector, std::string_view prefix)
Parse tag selector.
ClimbingContactTarget
Contact target used by animation/IK adapters.
Definition Climbing.h:115
ClimbingPhase
Execution lifecycle visible to gameplay and animation adapters.
Definition Climbing.h:470
ClimbingActionKind
Data-driven action family; stable action identity remains the definition id.
Definition Climbing.h:46
Definition of one geometry-compatible climbing action.
Definition Climbing.h:160
Owning, replayable climbing candidate; contains no borrowed query pointers.
Definition Climbing.h:290
Normalized contact interval and maximum constraint weight.
Definition Climbing.h:118
Input pose used for one deterministic candidate probe.
Definition Climbing.h:275
Runtime profile controlling capsule geometry and probe limits.
Definition Climbing.h:218
std::vector< std::string > deniedActionTags
Definition Climbing.h:250
std::vector< std::string > defaultActionIds
Definition Climbing.h:248
std::vector< ClimbingActionDefinition > actions
Definition Climbing.h:255
std::vector< std::string > allowedActionTags
Definition Climbing.h:249
Allocation-free counters produced by the latest runtime workload.
Normalized action interval selecting which motion-warp channels may correct authored motion.
Definition Climbing.h:151
Small owning world-space vector used at climbing module boundaries.