| | | 1 | | //======================================================================= |
| | | 2 | | // NavigationSelectedEdgeProgressWork.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2024-present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using System; |
| | | 9 | | using FixedMathSharp; |
| | | 10 | | using GridForge.Grids; |
| | | 11 | | using GridForge.Grids.Topology; |
| | | 12 | | |
| | | 13 | | namespace Trailblazer.Pathing; |
| | | 14 | | |
| | | 15 | | internal readonly struct NavigationFlowRejoinTarget |
| | | 16 | | { |
| | | 17 | | internal NavigationFlowRejoinTarget( |
| | | 18 | | Vector3d position, |
| | | 19 | | NavigationRayChainConstraint constraint) |
| | | 20 | | { |
| | | 21 | | Position = position; |
| | | 22 | | Constraint = constraint; |
| | | 23 | | } |
| | | 24 | | |
| | | 25 | | internal Vector3d Position { get; } |
| | | 26 | | |
| | | 27 | | internal NavigationRayChainConstraint Constraint { get; } |
| | | 28 | | } |
| | | 29 | | |
| | | 30 | | /// <summary>Samples only the canonical selected edge stored by one flow node.</summary> |
| | | 31 | | internal enum NavigationFlowNodeLookupStatus : byte |
| | | 32 | | { |
| | | 33 | | Success, |
| | | 34 | | NotFound, |
| | | 35 | | BudgetExceeded |
| | | 36 | | } |
| | | 37 | | |
| | | 38 | | internal static class NavigationSelectedEdgeProgressWork |
| | | 39 | | { |
| | | 40 | | internal static NavigationGuideStatus MapNodeLookupStatus( |
| | | 41 | | NavigationFlowNodeLookupStatus status, |
| | 19 | 42 | | bool required) => status switch |
| | 19 | 43 | | { |
| | 2 | 44 | | NavigationFlowNodeLookupStatus.Success => NavigationGuideStatus.Success, |
| | 19 | 45 | | NavigationFlowNodeLookupStatus.BudgetExceeded => |
| | 14 | 46 | | NavigationGuideStatus.BudgetExceeded, |
| | 3 | 47 | | _ => required ? NavigationGuideStatus.Stale : NavigationGuideStatus.Success |
| | 19 | 48 | | }; |
| | | 49 | | |
| | | 50 | | internal static NavigationGuideStatus MapStructuralEdgeStatus( |
| | 2528 | 51 | | NavigationSurfaceEdgeAdvanceStatus status) => status switch |
| | 2528 | 52 | | { |
| | 1249 | 53 | | NavigationSurfaceEdgeAdvanceStatus.Pending => NavigationGuideStatus.Success, |
| | 1275 | 54 | | NavigationSurfaceEdgeAdvanceStatus.Edge => NavigationGuideStatus.Success, |
| | 3 | 55 | | NavigationSurfaceEdgeAdvanceStatus.Blocked => NavigationGuideStatus.BudgetExceeded, |
| | 1 | 56 | | _ => NavigationGuideStatus.Stale |
| | 2528 | 57 | | }; |
| | | 58 | | |
| | | 59 | | internal static bool TryGetRejoinTarget( |
| | | 60 | | NavigationCellAddress sourceAddress, |
| | | 61 | | NavigationNodeState sourceState, |
| | | 62 | | NavigationNodeState targetState, |
| | | 63 | | NavigationSelectedEdgeRef selected, |
| | | 64 | | Vector3d selectedExitTarget, |
| | | 65 | | int targetOrdinal, |
| | | 66 | | out NavigationFlowRejoinTarget target) |
| | | 67 | | { |
| | 315 | 68 | | SwiftThrowHelper.ThrowIfNegative(targetOrdinal, nameof(targetOrdinal)); |
| | 315 | 69 | | target = default; |
| | 315 | 70 | | if (targetOrdinal == 0) |
| | | 71 | | { |
| | 292 | 72 | | target = new NavigationFlowRejoinTarget( |
| | 292 | 73 | | sourceState.FootAnchor, |
| | 292 | 74 | | NavigationRayChainConstraint.SourceOnly(sourceAddress)); |
| | 292 | 75 | | return true; |
| | | 76 | | } |
| | | 77 | | |
| | 23 | 78 | | NavigationRayChainConstraint constraint = |
| | 23 | 79 | | NavigationRayChainConstraint.SelectedEdge( |
| | 23 | 80 | | sourceAddress, |
| | 23 | 81 | | selected.Target, |
| | 23 | 82 | | selected.CanonicalOutgoingOrdinal); |
| | 23 | 83 | | if (targetOrdinal == 1) |
| | | 84 | | { |
| | 11 | 85 | | target = new NavigationFlowRejoinTarget(selectedExitTarget, constraint); |
| | 11 | 86 | | return true; |
| | | 87 | | } |
| | 12 | 88 | | if (targetOrdinal == 2) |
| | | 89 | | { |
| | 6 | 90 | | target = new NavigationFlowRejoinTarget(targetState.FootAnchor, constraint); |
| | 6 | 91 | | return true; |
| | | 92 | | } |
| | 6 | 93 | | return false; |
| | | 94 | | } |
| | | 95 | | |
| | | 96 | | internal static NavigationGuideStatus TrySample( |
| | | 97 | | bool dependencyCurrent, |
| | | 98 | | GridWorld world, |
| | | 99 | | NavigationWorldGraphStore store, |
| | | 100 | | NavigationWorldGraph graph, |
| | | 101 | | NavigationFlowFieldPayload payload, |
| | | 102 | | NavigationCellAddress sourceAddress, |
| | | 103 | | TraversalMedium medium, |
| | | 104 | | Vector3d actualFootPosition, |
| | | 105 | | ref GuideSampleWorkMeter meter, |
| | | 106 | | GridCoveredAddressCursor coveredAddressCursor, |
| | | 107 | | GridCoveredAddressGeneration[] coveredAddressGenerations, |
| | | 108 | | GridCoveredAddress[] coveredAddressOutput, |
| | | 109 | | NavigationImmediateRayWorkspace immediateRayWorkspace, |
| | | 110 | | out NavigationFlowFieldNode currentNode, |
| | | 111 | | out NavigationCellAddress nextSourceAddress, |
| | | 112 | | out Vector3d target, |
| | | 113 | | out Vector3d heading) |
| | | 114 | | { |
| | 1357 | 115 | | currentNode = default; |
| | 1357 | 116 | | nextSourceAddress = sourceAddress; |
| | 1357 | 117 | | target = default; |
| | 1357 | 118 | | heading = Vector3d.Zero; |
| | 1357 | 119 | | if (!dependencyCurrent) |
| | 1 | 120 | | return NavigationGuideStatus.Stale; |
| | 1356 | 121 | | if (medium is TraversalMedium.Gas or TraversalMedium.Liquid) |
| | | 122 | | { |
| | 102 | 123 | | return TrySampleVolume( |
| | 102 | 124 | | world, |
| | 102 | 125 | | store, |
| | 102 | 126 | | graph, |
| | 102 | 127 | | payload, |
| | 102 | 128 | | sourceAddress, |
| | 102 | 129 | | medium, |
| | 102 | 130 | | actualFootPosition, |
| | 102 | 131 | | ref meter, |
| | 102 | 132 | | immediateRayWorkspace, |
| | 102 | 133 | | out currentNode, |
| | 102 | 134 | | out nextSourceAddress, |
| | 102 | 135 | | out target, |
| | 102 | 136 | | out heading); |
| | | 137 | | } |
| | 1254 | 138 | | NavigationCellAddress currentSource = sourceAddress; |
| | 1254 | 139 | | bool allowRecovery = true; |
| | 4 | 140 | | while (true) |
| | | 141 | | { |
| | 1813 | 142 | | NavigationFlowNodeLookupStatus lookup = TryGetNode( |
| | 1813 | 143 | | payload, |
| | 1813 | 144 | | currentSource, |
| | 1813 | 145 | | medium, |
| | 1813 | 146 | | ref meter, |
| | 1813 | 147 | | out NavigationFlowFieldNode node); |
| | 1813 | 148 | | if (lookup != NavigationFlowNodeLookupStatus.Success) |
| | | 149 | | { |
| | 11 | 150 | | return MapNodeLookupStatus(lookup, required: true); |
| | | 151 | | } |
| | 1802 | 152 | | currentNode = node; |
| | 1802 | 153 | | if (node.TransitionInstructionOrdinal >= 0) |
| | | 154 | | { |
| | 15 | 155 | | nextSourceAddress = currentSource; |
| | 15 | 156 | | return NavigationGuideStatus.Success; |
| | | 157 | | } |
| | 1787 | 158 | | graph.TryGetNodeRef(currentSource, out NavigationNodeRef sourceRef); |
| | 1787 | 159 | | graph.TryGetNodeState( |
| | 1787 | 160 | | sourceRef, |
| | 1787 | 161 | | medium, |
| | 1787 | 162 | | out NavigationNodeState sourceState); |
| | 1787 | 163 | | graph.TryGetSeamPrism(currentSource, out GridCellPrism sourcePrism); |
| | 1787 | 164 | | if (!meter.TryConsumePrismChecks(1)) |
| | 2 | 165 | | return NavigationGuideStatus.BudgetExceeded; |
| | 1785 | 166 | | bool sourceContains = sourcePrism.Contains(actualFootPosition); |
| | 1785 | 167 | | target = sourceState.FootAnchor; |
| | 1785 | 168 | | if (currentSource == payload.Key.DestinationAddress) |
| | | 169 | | { |
| | 561 | 170 | | if (sourceContains |
| | 561 | 171 | | && GridCellGeometry.IsNavigationBodyAnchorValid( |
| | 561 | 172 | | sourcePrism, |
| | 561 | 173 | | actualFootPosition, |
| | 561 | 174 | | payload.Key.Agent.Shape.Radius, |
| | 561 | 175 | | payload.Key.Agent.Shape.Height, |
| | 561 | 176 | | default)) |
| | | 177 | | { |
| | 545 | 178 | | nextSourceAddress = currentSource; |
| | 545 | 179 | | if (IsWithinArrivalRadius( |
| | 545 | 180 | | actualFootPosition, |
| | 545 | 181 | | sourceState.FootAnchor, |
| | 545 | 182 | | payload.Key.Agent.ArrivalRadius)) |
| | | 183 | | { |
| | 542 | 184 | | heading = Vector3d.Zero; |
| | 542 | 185 | | return NavigationGuideStatus.Success; |
| | | 186 | | } |
| | 3 | 187 | | return TrySetHeading( |
| | 3 | 188 | | actualFootPosition, |
| | 3 | 189 | | sourceState.FootAnchor, |
| | 3 | 190 | | ref meter, |
| | 3 | 191 | | out heading); |
| | | 192 | | } |
| | 16 | 193 | | NavigationGuideStatus destinationRecovery = TryAdvanceRecovery( |
| | 16 | 194 | | world, |
| | 16 | 195 | | store, |
| | 16 | 196 | | graph, |
| | 16 | 197 | | payload, |
| | 16 | 198 | | medium, |
| | 16 | 199 | | actualFootPosition, |
| | 16 | 200 | | ref meter, |
| | 16 | 201 | | coveredAddressCursor, |
| | 16 | 202 | | coveredAddressGenerations, |
| | 16 | 203 | | coveredAddressOutput, |
| | 16 | 204 | | immediateRayWorkspace, |
| | 16 | 205 | | currentSource, |
| | 16 | 206 | | sourceState, |
| | 16 | 207 | | default, |
| | 16 | 208 | | Vector3d.Zero, |
| | 16 | 209 | | default, |
| | 16 | 210 | | hasSelectedEdge: false, |
| | 16 | 211 | | allowRecovery, |
| | 16 | 212 | | out currentSource, |
| | 16 | 213 | | out Vector3d destinationRejoinHeading, |
| | 16 | 214 | | out bool destinationRejoined); |
| | 16 | 215 | | if (destinationRecovery != NavigationGuideStatus.Success) |
| | 8 | 216 | | return destinationRecovery; |
| | 8 | 217 | | if (destinationRejoined) |
| | | 218 | | { |
| | 1 | 219 | | nextSourceAddress = sourceAddress; |
| | 1 | 220 | | heading = destinationRejoinHeading; |
| | 1 | 221 | | return NavigationGuideStatus.Success; |
| | | 222 | | } |
| | 7 | 223 | | allowRecovery = false; |
| | 7 | 224 | | continue; |
| | | 225 | | } |
| | | 226 | | |
| | 1224 | 227 | | NavigationGuideStatus edgeStatus = TryResolveSelectedEdge( |
| | 1224 | 228 | | graph, |
| | 1224 | 229 | | sourceRef, |
| | 1224 | 230 | | node.SelectedEdge, |
| | 1224 | 231 | | ref meter, |
| | 1224 | 232 | | out NavigationGraphEdge edge); |
| | 1224 | 233 | | if (edgeStatus != NavigationGuideStatus.Success) |
| | 2 | 234 | | return edgeStatus; |
| | 1222 | 235 | | graph.TryGetNodeState( |
| | 1222 | 236 | | edge.Target, |
| | 1222 | 237 | | medium, |
| | 1222 | 238 | | out NavigationNodeState targetState); |
| | 1222 | 239 | | graph.TryGetSeamPrism( |
| | 1222 | 240 | | node.SelectedEdge.Target, |
| | 1222 | 241 | | out GridCellPrism targetPrism); |
| | 1222 | 242 | | if (!meter.TryConsumePrismChecks(1)) |
| | 2 | 243 | | return NavigationGuideStatus.BudgetExceeded; |
| | 1220 | 244 | | bool targetContains = targetPrism.Contains(actualFootPosition); |
| | 1220 | 245 | | target = targetState.FootAnchor; |
| | | 246 | | |
| | 1220 | 247 | | if (edge.Kind == NavigationGraphEdgeKind.Explicit) |
| | | 248 | | { |
| | 30 | 249 | | NavigationGuideStatus explicitStatus = TrySampleExplicit( |
| | 30 | 250 | | graph, |
| | 30 | 251 | | edge.ExplicitConnection, |
| | 30 | 252 | | currentSource, |
| | 30 | 253 | | sourceState, |
| | 30 | 254 | | sourcePrism, |
| | 30 | 255 | | targetState, |
| | 30 | 256 | | targetPrism, |
| | 30 | 257 | | sourceContains, |
| | 30 | 258 | | targetContains, |
| | 30 | 259 | | actualFootPosition, |
| | 30 | 260 | | payload.Key.Agent.Shape, |
| | 30 | 261 | | node.SelectedEdge.Target == payload.Key.DestinationAddress, |
| | 30 | 262 | | payload.Key.Agent.ArrivalRadius, |
| | 30 | 263 | | ref meter, |
| | 30 | 264 | | out NavigationCellAddress explicitSource, |
| | 30 | 265 | | out heading); |
| | 30 | 266 | | if (explicitStatus == NavigationGuideStatus.LocalRecoveryRequired) |
| | | 267 | | { |
| | 6 | 268 | | NavigationGuideStatus recovery = TryAdvanceRecovery( |
| | 6 | 269 | | world, |
| | 6 | 270 | | store, |
| | 6 | 271 | | graph, |
| | 6 | 272 | | payload, |
| | 6 | 273 | | medium, |
| | 6 | 274 | | actualFootPosition, |
| | 6 | 275 | | ref meter, |
| | 6 | 276 | | coveredAddressCursor, |
| | 6 | 277 | | coveredAddressGenerations, |
| | 6 | 278 | | coveredAddressOutput, |
| | 6 | 279 | | immediateRayWorkspace, |
| | 6 | 280 | | currentSource, |
| | 6 | 281 | | sourceState, |
| | 6 | 282 | | node.SelectedEdge, |
| | 6 | 283 | | edge.ExplicitConnection.Definition.ExitAnchor, |
| | 6 | 284 | | targetState, |
| | 6 | 285 | | hasSelectedEdge: true, |
| | 6 | 286 | | allowRecovery, |
| | 6 | 287 | | out currentSource, |
| | 6 | 288 | | out Vector3d rejoinHeading, |
| | 6 | 289 | | out bool rejoined); |
| | 6 | 290 | | if (recovery != NavigationGuideStatus.Success) |
| | 2 | 291 | | return recovery; |
| | 4 | 292 | | if (rejoined) |
| | | 293 | | { |
| | 2 | 294 | | nextSourceAddress = sourceAddress; |
| | 2 | 295 | | heading = rejoinHeading; |
| | 2 | 296 | | return NavigationGuideStatus.Success; |
| | | 297 | | } |
| | 2 | 298 | | allowRecovery = false; |
| | 2 | 299 | | continue; |
| | | 300 | | } |
| | 24 | 301 | | if (explicitStatus != NavigationGuideStatus.Success |
| | 24 | 302 | | || explicitSource == currentSource) |
| | | 303 | | { |
| | 22 | 304 | | nextSourceAddress = currentSource; |
| | 22 | 305 | | return explicitStatus; |
| | | 306 | | } |
| | 2 | 307 | | currentSource = explicitSource; |
| | 2 | 308 | | continue; |
| | | 309 | | } |
| | | 310 | | |
| | 1190 | 311 | | NavigationGuideStatus portalStatus = TryResolvePortal( |
| | 1190 | 312 | | edge, |
| | 1190 | 313 | | sourceState, |
| | 1190 | 314 | | payload.Key.Agent.Shape, |
| | 1190 | 315 | | ref meter, |
| | 1190 | 316 | | out GridNavigationPortal selectedPortal, |
| | 1190 | 317 | | out Vector3d sourcePortal, |
| | 1190 | 318 | | out Vector3d targetPortal); |
| | 1190 | 319 | | if (portalStatus != NavigationGuideStatus.Success) |
| | 11 | 320 | | return portalStatus; |
| | 1179 | 321 | | bool sourceBodyValid = sourceContains |
| | 1179 | 322 | | && GridCellGeometry.IsNavigationBodyAnchorValid( |
| | 1179 | 323 | | sourcePrism, |
| | 1179 | 324 | | actualFootPosition, |
| | 1179 | 325 | | payload.Key.Agent.Shape.Radius, |
| | 1179 | 326 | | payload.Key.Agent.Shape.Height, |
| | 1179 | 327 | | selectedPortal); |
| | 1179 | 328 | | bool targetBodyValid = targetContains |
| | 1179 | 329 | | && GridCellGeometry.IsNavigationBodyAnchorValid( |
| | 1179 | 330 | | targetPrism, |
| | 1179 | 331 | | actualFootPosition, |
| | 1179 | 332 | | payload.Key.Agent.Shape.Radius, |
| | 1179 | 333 | | payload.Key.Agent.Shape.Height, |
| | 1179 | 334 | | selectedPortal); |
| | 1179 | 335 | | if (!sourceBodyValid && !targetBodyValid) |
| | | 336 | | { |
| | 840 | 337 | | NavigationGuideStatus recovery = TryAdvanceRecovery( |
| | 840 | 338 | | world, |
| | 840 | 339 | | store, |
| | 840 | 340 | | graph, |
| | 840 | 341 | | payload, |
| | 840 | 342 | | medium, |
| | 840 | 343 | | actualFootPosition, |
| | 840 | 344 | | ref meter, |
| | 840 | 345 | | coveredAddressCursor, |
| | 840 | 346 | | coveredAddressGenerations, |
| | 840 | 347 | | coveredAddressOutput, |
| | 840 | 348 | | immediateRayWorkspace, |
| | 840 | 349 | | currentSource, |
| | 840 | 350 | | sourceState, |
| | 840 | 351 | | node.SelectedEdge, |
| | 840 | 352 | | targetPortal, |
| | 840 | 353 | | targetState, |
| | 840 | 354 | | hasSelectedEdge: true, |
| | 840 | 355 | | allowRecovery, |
| | 840 | 356 | | out currentSource, |
| | 840 | 357 | | out Vector3d rejoinHeading, |
| | 840 | 358 | | out bool rejoined); |
| | 840 | 359 | | if (recovery != NavigationGuideStatus.Success) |
| | 14 | 360 | | return recovery; |
| | 826 | 361 | | if (rejoined) |
| | | 362 | | { |
| | 282 | 363 | | nextSourceAddress = sourceAddress; |
| | 282 | 364 | | heading = rejoinHeading; |
| | 282 | 365 | | return NavigationGuideStatus.Success; |
| | | 366 | | } |
| | 544 | 367 | | allowRecovery = false; |
| | 544 | 368 | | continue; |
| | | 369 | | } |
| | 339 | 370 | | if (targetBodyValid |
| | 339 | 371 | | && node.SelectedEdge.Target == payload.Key.DestinationAddress |
| | 339 | 372 | | && IsWithinArrivalRadius( |
| | 339 | 373 | | actualFootPosition, |
| | 339 | 374 | | targetState.FootAnchor, |
| | 339 | 375 | | payload.Key.Agent.ArrivalRadius)) |
| | | 376 | | { |
| | 2 | 377 | | nextSourceAddress = currentSource; |
| | 2 | 378 | | heading = Vector3d.Zero; |
| | 2 | 379 | | return NavigationGuideStatus.Success; |
| | | 380 | | } |
| | 337 | 381 | | if (sourceBodyValid) |
| | | 382 | | { |
| | 328 | 383 | | nextSourceAddress = currentSource; |
| | 328 | 384 | | return TrySampleDirectedLeg( |
| | 328 | 385 | | sourceState.FootAnchor, |
| | 328 | 386 | | sourcePortal, |
| | 328 | 387 | | targetPortal, |
| | 328 | 388 | | targetState.FootAnchor, |
| | 328 | 389 | | actualFootPosition, |
| | 328 | 390 | | ref meter, |
| | 328 | 391 | | out heading); |
| | | 392 | | } |
| | | 393 | | |
| | 9 | 394 | | NavigationGuideStatus targetProgress = HasReachedOrPassed( |
| | 9 | 395 | | targetPortal, |
| | 9 | 396 | | targetState.FootAnchor, |
| | 9 | 397 | | actualFootPosition, |
| | 9 | 398 | | out bool passedTarget); |
| | 9 | 399 | | if (targetProgress != NavigationGuideStatus.Success) |
| | 1 | 400 | | return targetProgress; |
| | 8 | 401 | | if (!passedTarget) |
| | | 402 | | { |
| | 2 | 403 | | nextSourceAddress = currentSource; |
| | 2 | 404 | | return TrySetHeading( |
| | 2 | 405 | | actualFootPosition, |
| | 2 | 406 | | targetState.FootAnchor, |
| | 2 | 407 | | ref meter, |
| | 2 | 408 | | out heading); |
| | | 409 | | } |
| | 6 | 410 | | if (!meter.TryConsumeCursorRebases(1)) |
| | 2 | 411 | | return NavigationGuideStatus.BudgetExceeded; |
| | 4 | 412 | | currentSource = node.SelectedEdge.Target; |
| | | 413 | | } |
| | | 414 | | } |
| | | 415 | | |
| | | 416 | | private static NavigationGuideStatus TryResolveSelectedEdge( |
| | | 417 | | NavigationWorldGraph graph, |
| | | 418 | | NavigationNodeRef source, |
| | | 419 | | NavigationSelectedEdgeRef selected, |
| | | 420 | | ref GuideSampleWorkMeter meter, |
| | | 421 | | out NavigationGraphEdge edge) |
| | | 422 | | { |
| | 1224 | 423 | | NavigationSurfaceEdgeEnumerator edges = graph.EnumerateStructuralSurfaceEdges(source); |
| | 1224 | 424 | | int edgeStepRemaining = int.MaxValue; |
| | | 425 | | while (true) |
| | | 426 | | { |
| | 2524 | 427 | | NavigationSurfaceEdgeAdvanceStatus status = edges.AdvanceOne( |
| | 2524 | 428 | | ref meter, |
| | 2524 | 429 | | ref edgeStepRemaining); |
| | 2524 | 430 | | NavigationGuideStatus mappedStatus = MapStructuralEdgeStatus(status); |
| | 2524 | 431 | | if (mappedStatus != NavigationGuideStatus.Success) |
| | | 432 | | { |
| | 2 | 433 | | edge = default; |
| | 2 | 434 | | return mappedStatus; |
| | | 435 | | } |
| | 2522 | 436 | | if (status == NavigationSurfaceEdgeAdvanceStatus.Pending) |
| | | 437 | | continue; |
| | 1274 | 438 | | if (edges.CurrentOrdinal != selected.CanonicalOutgoingOrdinal) |
| | | 439 | | continue; |
| | 1222 | 440 | | edge = edges.Current; |
| | 1222 | 441 | | return NavigationGuideStatus.Success; |
| | | 442 | | } |
| | | 443 | | } |
| | | 444 | | |
| | | 445 | | private static NavigationGuideStatus TrySampleVolume( |
| | | 446 | | GridWorld world, |
| | | 447 | | NavigationWorldGraphStore store, |
| | | 448 | | NavigationWorldGraph graph, |
| | | 449 | | NavigationFlowFieldPayload payload, |
| | | 450 | | NavigationCellAddress sourceAddress, |
| | | 451 | | TraversalMedium medium, |
| | | 452 | | Vector3d actualFootPosition, |
| | | 453 | | ref GuideSampleWorkMeter meter, |
| | | 454 | | NavigationImmediateRayWorkspace immediateRayWorkspace, |
| | | 455 | | out NavigationFlowFieldNode currentNode, |
| | | 456 | | out NavigationCellAddress nextSourceAddress, |
| | | 457 | | out Vector3d target, |
| | | 458 | | out Vector3d heading) |
| | | 459 | | { |
| | 102 | 460 | | currentNode = default; |
| | 102 | 461 | | nextSourceAddress = sourceAddress; |
| | 102 | 462 | | target = default; |
| | 102 | 463 | | heading = Vector3d.Zero; |
| | 102 | 464 | | NavigationCellAddress currentSource = sourceAddress; |
| | 102 | 465 | | graph.AreaCatalog.TryGet( |
| | 102 | 466 | | payload.Key.AreaPolicy, |
| | 102 | 467 | | out NavigationAreaPolicy areaPolicy); |
| | | 468 | | while (true) |
| | | 469 | | { |
| | 107 | 470 | | NavigationFlowNodeLookupStatus lookup = TryGetNode( |
| | 107 | 471 | | payload, |
| | 107 | 472 | | currentSource, |
| | 107 | 473 | | medium, |
| | 107 | 474 | | ref meter, |
| | 107 | 475 | | out NavigationFlowFieldNode node); |
| | 107 | 476 | | if (lookup != NavigationFlowNodeLookupStatus.Success) |
| | | 477 | | { |
| | 1 | 478 | | return MapNodeLookupStatus(lookup, required: true); |
| | | 479 | | } |
| | 106 | 480 | | currentNode = node; |
| | 106 | 481 | | if (node.TransitionInstructionOrdinal >= 0) |
| | | 482 | | { |
| | 7 | 483 | | nextSourceAddress = currentSource; |
| | 7 | 484 | | return NavigationGuideStatus.Success; |
| | | 485 | | } |
| | 99 | 486 | | graph.TryGetNodeRef(currentSource, out NavigationNodeRef sourceRef); |
| | 99 | 487 | | graph.TryGetNodeState( |
| | 99 | 488 | | sourceRef, |
| | 99 | 489 | | medium, |
| | 99 | 490 | | out NavigationNodeState sourceState); |
| | 99 | 491 | | sourceState.TryGetCenteredVolumeFootAnchor( |
| | 99 | 492 | | payload.Key.Agent.Shape.Height, |
| | 99 | 493 | | out Vector3d sourceAnchor); |
| | 99 | 494 | | target = sourceAnchor; |
| | 99 | 495 | | if (!node.SelectedEdge.IsValid) |
| | | 496 | | { |
| | 6 | 497 | | nextSourceAddress = currentSource; |
| | 6 | 498 | | NavigationGuideStatus destination = RunRay( |
| | 6 | 499 | | world, |
| | 6 | 500 | | store, |
| | 6 | 501 | | graph, |
| | 6 | 502 | | payload, |
| | 6 | 503 | | areaPolicy, |
| | 6 | 504 | | medium, |
| | 6 | 505 | | actualFootPosition, |
| | 6 | 506 | | sourceAnchor, |
| | 6 | 507 | | NavigationRayChainConstraint.SourceOnly(currentSource), |
| | 6 | 508 | | ref meter, |
| | 6 | 509 | | immediateRayWorkspace); |
| | 6 | 510 | | if (destination != NavigationGuideStatus.Success) |
| | 1 | 511 | | return destination; |
| | 5 | 512 | | if (IsWithinArrivalRadius( |
| | 5 | 513 | | actualFootPosition, |
| | 5 | 514 | | sourceAnchor, |
| | 5 | 515 | | payload.Key.Agent.ArrivalRadius)) |
| | | 516 | | { |
| | 4 | 517 | | heading = Vector3d.Zero; |
| | 4 | 518 | | return NavigationGuideStatus.Success; |
| | | 519 | | } |
| | 1 | 520 | | return TrySetHeadingUnchecked(actualFootPosition, sourceAnchor, out heading); |
| | | 521 | | } |
| | 93 | 522 | | graph.TryGetNodeRef( |
| | 93 | 523 | | node.SelectedEdge.Target, |
| | 93 | 524 | | out NavigationNodeRef targetRef); |
| | 93 | 525 | | graph.TryGetNodeState( |
| | 93 | 526 | | targetRef, |
| | 93 | 527 | | medium, |
| | 93 | 528 | | out NavigationNodeState targetState); |
| | 93 | 529 | | targetState.TryGetCenteredVolumeFootAnchor( |
| | 93 | 530 | | payload.Key.Agent.Shape.Height, |
| | 93 | 531 | | out Vector3d targetAnchor); |
| | 93 | 532 | | target = targetAnchor; |
| | 93 | 533 | | if (actualFootPosition == targetAnchor) |
| | | 534 | | { |
| | 8 | 535 | | NavigationGuideStatus arrived = RunRay( |
| | 8 | 536 | | world, |
| | 8 | 537 | | store, |
| | 8 | 538 | | graph, |
| | 8 | 539 | | payload, |
| | 8 | 540 | | areaPolicy, |
| | 8 | 541 | | medium, |
| | 8 | 542 | | actualFootPosition, |
| | 8 | 543 | | actualFootPosition, |
| | 8 | 544 | | NavigationRayChainConstraint.SourceOnly(node.SelectedEdge.Target), |
| | 8 | 545 | | ref meter, |
| | 8 | 546 | | immediateRayWorkspace); |
| | 8 | 547 | | if (arrived != NavigationGuideStatus.Success) |
| | 1 | 548 | | return arrived; |
| | 7 | 549 | | if (!meter.TryConsumeCursorRebases(1)) |
| | 2 | 550 | | return NavigationGuideStatus.BudgetExceeded; |
| | 5 | 551 | | currentSource = node.SelectedEdge.Target; |
| | 5 | 552 | | nextSourceAddress = currentSource; |
| | 5 | 553 | | continue; |
| | | 554 | | } |
| | | 555 | | |
| | 85 | 556 | | NavigationGuideStatus status = RunRay( |
| | 85 | 557 | | world, |
| | 85 | 558 | | store, |
| | 85 | 559 | | graph, |
| | 85 | 560 | | payload, |
| | 85 | 561 | | areaPolicy, |
| | 85 | 562 | | medium, |
| | 85 | 563 | | actualFootPosition, |
| | 85 | 564 | | targetAnchor, |
| | 85 | 565 | | NavigationRayChainConstraint.SelectedEdge( |
| | 85 | 566 | | currentSource, |
| | 85 | 567 | | node.SelectedEdge.Target, |
| | 85 | 568 | | node.SelectedEdge.CanonicalOutgoingOrdinal), |
| | 85 | 569 | | ref meter, |
| | 85 | 570 | | immediateRayWorkspace); |
| | 85 | 571 | | if (status != NavigationGuideStatus.Success) |
| | 2 | 572 | | return status; |
| | 83 | 573 | | nextSourceAddress = currentSource; |
| | 83 | 574 | | if (node.SelectedEdge.Target == payload.Key.DestinationAddress |
| | 83 | 575 | | && IsWithinArrivalRadius( |
| | 83 | 576 | | actualFootPosition, |
| | 83 | 577 | | targetAnchor, |
| | 83 | 578 | | payload.Key.Agent.ArrivalRadius)) |
| | | 579 | | { |
| | 1 | 580 | | heading = Vector3d.Zero; |
| | 1 | 581 | | return NavigationGuideStatus.Success; |
| | | 582 | | } |
| | 82 | 583 | | return TrySetHeadingUnchecked(actualFootPosition, targetAnchor, out heading); |
| | | 584 | | } |
| | | 585 | | } |
| | | 586 | | |
| | | 587 | | private static bool IsWithinArrivalRadius( |
| | | 588 | | Vector3d actualFootPosition, |
| | | 589 | | Vector3d target, |
| | | 590 | | Fixed64 arrivalRadius) => |
| | 651 | 591 | | Vector3d.CompareDistanceSquared( |
| | 651 | 592 | | actualFootPosition, |
| | 651 | 593 | | target, |
| | 651 | 594 | | Vector3d.Zero, |
| | 651 | 595 | | new Vector3d(arrivalRadius, Fixed64.Zero, Fixed64.Zero)) <= 0; |
| | | 596 | | |
| | | 597 | | internal static NavigationGuideStatus TrySampleTransitionApproach( |
| | | 598 | | GridWorld world, |
| | | 599 | | NavigationWorldGraphStore store, |
| | | 600 | | NavigationWorldGraph graph, |
| | | 601 | | NavigationFlowFieldPayload payload, |
| | | 602 | | NavigationCellAddress source, |
| | | 603 | | TraversalMedium medium, |
| | | 604 | | Vector3d actualFootPosition, |
| | | 605 | | Vector3d actionPosition, |
| | | 606 | | ref GuideSampleWorkMeter meter, |
| | | 607 | | NavigationImmediateRayWorkspace immediateRayWorkspace, |
| | | 608 | | out Vector3d heading) |
| | | 609 | | { |
| | 21 | 610 | | heading = Vector3d.Zero; |
| | 21 | 611 | | bool alreadyAtAction = actualFootPosition == actionPosition; |
| | 21 | 612 | | graph.AreaCatalog.TryGet( |
| | 21 | 613 | | payload.Key.AreaPolicy, |
| | 21 | 614 | | out NavigationAreaPolicy areaPolicy); |
| | 21 | 615 | | NavigationGuideStatus status = RunRay( |
| | 21 | 616 | | world, |
| | 21 | 617 | | store, |
| | 21 | 618 | | graph, |
| | 21 | 619 | | payload, |
| | 21 | 620 | | areaPolicy, |
| | 21 | 621 | | medium, |
| | 21 | 622 | | actualFootPosition, |
| | 21 | 623 | | actionPosition, |
| | 21 | 624 | | NavigationRayChainConstraint.SourceOnly(source), |
| | 21 | 625 | | ref meter, |
| | 21 | 626 | | immediateRayWorkspace); |
| | 21 | 627 | | if (alreadyAtAction && status == NavigationGuideStatus.LocalRecoveryRequired) |
| | 4 | 628 | | return NavigationGuideStatus.Success; |
| | 17 | 629 | | return status == NavigationGuideStatus.Success |
| | 17 | 630 | | ? TrySetHeadingUnchecked(actualFootPosition, actionPosition, out heading) |
| | 17 | 631 | | : status; |
| | | 632 | | } |
| | | 633 | | |
| | | 634 | | private static NavigationGuideStatus RunRay( |
| | | 635 | | GridWorld world, |
| | | 636 | | NavigationWorldGraphStore store, |
| | | 637 | | NavigationWorldGraph graph, |
| | | 638 | | NavigationFlowFieldPayload payload, |
| | | 639 | | NavigationAreaPolicy areaPolicy, |
| | | 640 | | TraversalMedium medium, |
| | | 641 | | Vector3d start, |
| | | 642 | | Vector3d end, |
| | | 643 | | NavigationRayChainConstraint constraint, |
| | | 644 | | ref GuideSampleWorkMeter meter, |
| | | 645 | | NavigationImmediateRayWorkspace immediateRayWorkspace) |
| | | 646 | | { |
| | 120 | 647 | | lock (immediateRayWorkspace.SyncRoot) |
| | | 648 | | { |
| | 120 | 649 | | NavigationWorkMeter rayMeter = immediateRayWorkspace.WorkMeter; |
| | 120 | 650 | | rayMeter.ResetForGuideSample( |
| | 120 | 651 | | meter.GetCurrentNodeLookupAllowance(), |
| | 120 | 652 | | meter.GetCursorLegScanAllowance(), |
| | 120 | 653 | | meter.GetPortalCheckAllowance(), |
| | 120 | 654 | | meter.GetPrismCheckAllowance(), |
| | 120 | 655 | | meter.GetTraceIntervalAllowance()); |
| | 120 | 656 | | NavigationRayWork ray = immediateRayWorkspace.RayWork; |
| | 120 | 657 | | ray.Begin(new NavigationRayRequest( |
| | 120 | 658 | | world, |
| | 120 | 659 | | store, |
| | 120 | 660 | | graph, |
| | 120 | 661 | | payload.Key.Agent, |
| | 120 | 662 | | areaPolicy, |
| | 120 | 663 | | medium, |
| | 120 | 664 | | start, |
| | 120 | 665 | | end, |
| | 120 | 666 | | NavigationRayEndpointAllowance.StartPrefix, |
| | 120 | 667 | | constraint)); |
| | | 668 | | NavigationRayStatus status; |
| | | 669 | | try |
| | | 670 | | { |
| | | 671 | | do |
| | | 672 | | { |
| | 120 | 673 | | status = ray.Advance(rayMeter); |
| | | 674 | | } |
| | 120 | 675 | | while (status == NavigationRayStatus.Pending); |
| | 120 | 676 | | } |
| | | 677 | | finally |
| | | 678 | | { |
| | 120 | 679 | | ray.Reset(); |
| | 120 | 680 | | } |
| | 120 | 681 | | meter.TryConsumeCurrentNodeLookupProbes( |
| | 120 | 682 | | checked(rayMeter.LookupProbes + rayMeter.CoveredVoxelIntervals)); |
| | 120 | 683 | | meter.TryConsumeCursorLegScans( |
| | 120 | 684 | | checked(rayMeter.EvaluatedEdges + rayMeter.ConnectionLegs)); |
| | 120 | 685 | | meter.TryConsumePortalChecks(rayMeter.GuidePortalChecks); |
| | 120 | 686 | | meter.TryConsumePrismChecks(rayMeter.GuidePrismChecks); |
| | 120 | 687 | | meter.TryConsumeTraceIntervals(rayMeter.TraceIntervals); |
| | 120 | 688 | | return status == NavigationRayStatus.Success |
| | 120 | 689 | | ? NavigationGuideStatus.Success |
| | 120 | 690 | | : NavigationGuideStatusMapper.ToPublic(status); |
| | | 691 | | } |
| | 120 | 692 | | } |
| | | 693 | | |
| | | 694 | | private static NavigationFlowNodeLookupStatus TryGetNode( |
| | | 695 | | NavigationFlowFieldPayload payload, |
| | | 696 | | NavigationCellAddress address, |
| | | 697 | | TraversalMedium medium, |
| | | 698 | | ref GuideSampleWorkMeter meter, |
| | | 699 | | out NavigationFlowFieldNode node) |
| | | 700 | | { |
| | 2475 | 701 | | int low = 0; |
| | 2475 | 702 | | int high = payload.AddressLookupOrdinals.Length - 1; |
| | 6051 | 703 | | while (low <= high) |
| | | 704 | | { |
| | 6050 | 705 | | if (!meter.TryConsumeCurrentNodeLookupProbes(1)) |
| | | 706 | | { |
| | 12 | 707 | | node = default; |
| | 12 | 708 | | return NavigationFlowNodeLookupStatus.BudgetExceeded; |
| | | 709 | | } |
| | 6038 | 710 | | int middle = low + ((high - low) >> 1); |
| | 6038 | 711 | | NavigationFlowFieldNode candidate = |
| | 6038 | 712 | | payload.Nodes[payload.AddressLookupOrdinals[middle]]; |
| | 6038 | 713 | | int comparison = candidate.Address.CompareTo(address); |
| | 6038 | 714 | | if (comparison == 0) |
| | 2462 | 715 | | comparison = ((int)candidate.Medium).CompareTo((int)medium); |
| | 6038 | 716 | | if (comparison == 0) |
| | | 717 | | { |
| | 2462 | 718 | | node = candidate; |
| | 2462 | 719 | | return NavigationFlowNodeLookupStatus.Success; |
| | | 720 | | } |
| | 3576 | 721 | | if (comparison < 0) |
| | 2344 | 722 | | low = middle + 1; |
| | | 723 | | else |
| | 1232 | 724 | | high = middle - 1; |
| | | 725 | | } |
| | 1 | 726 | | node = default; |
| | 1 | 727 | | return NavigationFlowNodeLookupStatus.NotFound; |
| | | 728 | | } |
| | | 729 | | |
| | | 730 | | private static NavigationGuideStatus TryAdvanceRecovery( |
| | | 731 | | GridWorld world, |
| | | 732 | | NavigationWorldGraphStore store, |
| | | 733 | | NavigationWorldGraph graph, |
| | | 734 | | NavigationFlowFieldPayload payload, |
| | | 735 | | TraversalMedium medium, |
| | | 736 | | Vector3d actualFootPosition, |
| | | 737 | | ref GuideSampleWorkMeter meter, |
| | | 738 | | GridCoveredAddressCursor coveredAddressCursor, |
| | | 739 | | GridCoveredAddressGeneration[] coveredAddressGenerations, |
| | | 740 | | GridCoveredAddress[] coveredAddressOutput, |
| | | 741 | | NavigationImmediateRayWorkspace immediateRayWorkspace, |
| | | 742 | | NavigationCellAddress sourceAddress, |
| | | 743 | | NavigationNodeState sourceState, |
| | | 744 | | NavigationSelectedEdgeRef selected, |
| | | 745 | | Vector3d selectedExitTarget, |
| | | 746 | | NavigationNodeState targetState, |
| | | 747 | | bool hasSelectedEdge, |
| | | 748 | | bool allowRecovery, |
| | | 749 | | out NavigationCellAddress rebased, |
| | | 750 | | out Vector3d heading, |
| | | 751 | | out bool rejoined) |
| | | 752 | | { |
| | 862 | 753 | | rebased = default; |
| | 862 | 754 | | heading = Vector3d.Zero; |
| | 862 | 755 | | rejoined = false; |
| | 862 | 756 | | if (!allowRecovery) |
| | 5 | 757 | | return NavigationGuideStatus.LocalRecoveryRequired; |
| | 857 | 758 | | NavigationGuideStatus rebase = TryRebase( |
| | 857 | 759 | | world, |
| | 857 | 760 | | graph, |
| | 857 | 761 | | payload, |
| | 857 | 762 | | medium, |
| | 857 | 763 | | actualFootPosition, |
| | 857 | 764 | | ref meter, |
| | 857 | 765 | | coveredAddressCursor, |
| | 857 | 766 | | coveredAddressGenerations, |
| | 857 | 767 | | coveredAddressOutput, |
| | 857 | 768 | | out rebased); |
| | 857 | 769 | | if (rebase != NavigationGuideStatus.LocalRecoveryRequired) |
| | 566 | 770 | | return rebase; |
| | 291 | 771 | | NavigationGuideStatus status = TryRejoin( |
| | 291 | 772 | | world, |
| | 291 | 773 | | store, |
| | 291 | 774 | | graph, |
| | 291 | 775 | | payload, |
| | 291 | 776 | | medium, |
| | 291 | 777 | | sourceAddress, |
| | 291 | 778 | | sourceState, |
| | 291 | 779 | | selected, |
| | 291 | 780 | | selectedExitTarget, |
| | 291 | 781 | | targetState, |
| | 291 | 782 | | hasSelectedEdge, |
| | 291 | 783 | | actualFootPosition, |
| | 291 | 784 | | ref meter, |
| | 291 | 785 | | immediateRayWorkspace, |
| | 291 | 786 | | out heading); |
| | 291 | 787 | | rejoined = status == NavigationGuideStatus.Success; |
| | 291 | 788 | | return status; |
| | | 789 | | } |
| | | 790 | | |
| | | 791 | | private static NavigationGuideStatus TryRejoin( |
| | | 792 | | GridWorld world, |
| | | 793 | | NavigationWorldGraphStore store, |
| | | 794 | | NavigationWorldGraph graph, |
| | | 795 | | NavigationFlowFieldPayload payload, |
| | | 796 | | TraversalMedium medium, |
| | | 797 | | NavigationCellAddress sourceAddress, |
| | | 798 | | NavigationNodeState sourceState, |
| | | 799 | | NavigationSelectedEdgeRef selected, |
| | | 800 | | Vector3d selectedExitTarget, |
| | | 801 | | NavigationNodeState targetState, |
| | | 802 | | bool hasSelectedEdge, |
| | | 803 | | Vector3d actualFootPosition, |
| | | 804 | | ref GuideSampleWorkMeter meter, |
| | | 805 | | NavigationImmediateRayWorkspace immediateRayWorkspace, |
| | | 806 | | out Vector3d heading) |
| | | 807 | | { |
| | 291 | 808 | | heading = Vector3d.Zero; |
| | 291 | 809 | | graph.AreaCatalog.TryGet( |
| | 291 | 810 | | payload.Key.AreaPolicy, |
| | 291 | 811 | | out NavigationAreaPolicy areaPolicy); |
| | 291 | 812 | | lock (immediateRayWorkspace.SyncRoot) |
| | | 813 | | { |
| | 291 | 814 | | NavigationRayWork ray = immediateRayWorkspace.RayWork; |
| | 305 | 815 | | for (int targetOrdinal = 0; ; targetOrdinal++) |
| | | 816 | | { |
| | 305 | 817 | | if (targetOrdinal > 0 && !hasSelectedEdge) |
| | 1 | 818 | | return NavigationGuideStatus.LocalRecoveryRequired; |
| | 304 | 819 | | if (!TryGetRejoinTarget( |
| | 304 | 820 | | sourceAddress, |
| | 304 | 821 | | sourceState, |
| | 304 | 822 | | targetState, |
| | 304 | 823 | | selected, |
| | 304 | 824 | | selectedExitTarget, |
| | 304 | 825 | | targetOrdinal, |
| | 304 | 826 | | out NavigationFlowRejoinTarget target)) |
| | | 827 | | { |
| | 4 | 828 | | return NavigationGuideStatus.LocalRecoveryRequired; |
| | | 829 | | } |
| | | 830 | | |
| | 300 | 831 | | ray.Begin(new NavigationRayRequest( |
| | 300 | 832 | | world, |
| | 300 | 833 | | store, |
| | 300 | 834 | | graph, |
| | 300 | 835 | | payload.Key.Agent, |
| | 300 | 836 | | areaPolicy, |
| | 300 | 837 | | medium, |
| | 300 | 838 | | actualFootPosition, |
| | 300 | 839 | | target.Position, |
| | 300 | 840 | | NavigationRayEndpointAllowance.StartPrefix, |
| | 300 | 841 | | target.Constraint)); |
| | | 842 | | NavigationRayStatus status; |
| | | 843 | | NavigationRayResult result; |
| | | 844 | | try |
| | | 845 | | { |
| | | 846 | | do |
| | | 847 | | { |
| | 300 | 848 | | status = ray.Advance(ref meter); |
| | | 849 | | } |
| | 300 | 850 | | while (status == NavigationRayStatus.Pending); |
| | 300 | 851 | | result = ray.Result; |
| | 300 | 852 | | } |
| | | 853 | | finally |
| | | 854 | | { |
| | 300 | 855 | | ray.Reset(); |
| | 300 | 856 | | } |
| | 300 | 857 | | if (status == NavigationRayStatus.Blocked |
| | 300 | 858 | | || (status == NavigationRayStatus.Success |
| | 300 | 859 | | && !result.IsSemanticCostNeutral |
| | 300 | 860 | | && target.Constraint.Kind |
| | 300 | 861 | | != NavigationRayChainConstraintKind.SelectedEdge)) |
| | | 862 | | { |
| | | 863 | | continue; |
| | | 864 | | } |
| | 286 | 865 | | if (status != NavigationRayStatus.Success) |
| | 1 | 866 | | return NavigationGuideStatusMapper.ToPublic(status); |
| | 285 | 867 | | return TrySetHeadingUnchecked( |
| | 285 | 868 | | actualFootPosition, |
| | 285 | 869 | | target.Position, |
| | 285 | 870 | | out heading); |
| | | 871 | | } |
| | | 872 | | } |
| | 291 | 873 | | } |
| | | 874 | | |
| | | 875 | | private static NavigationGuideStatus TryResolvePortal( |
| | | 876 | | NavigationGraphEdge edge, |
| | | 877 | | NavigationNodeState sourceState, |
| | | 878 | | KinematicBodyShape shape, |
| | | 879 | | ref GuideSampleWorkMeter meter, |
| | | 880 | | out GridNavigationPortal resolvedPortal, |
| | | 881 | | out Vector3d sourcePortal, |
| | | 882 | | out Vector3d targetPortal) |
| | | 883 | | { |
| | 1190 | 884 | | resolvedPortal = default; |
| | 1190 | 885 | | sourcePortal = default; |
| | 1190 | 886 | | targetPortal = default; |
| | 1190 | 887 | | if (!meter.TryConsumePortalChecks(1)) |
| | 11 | 888 | | return NavigationGuideStatus.BudgetExceeded; |
| | 1179 | 889 | | bool reverse = false; |
| | 1179 | 890 | | if (edge.Kind == NavigationGraphEdgeKind.Native) |
| | | 891 | | { |
| | 1175 | 892 | | edge.NativePortal.TryTranslate(sourceState.Center, out resolvedPortal); |
| | | 893 | | } |
| | | 894 | | else |
| | | 895 | | { |
| | 4 | 896 | | NavigationAutomaticSeamRef seam = edge.AutomaticSeam; |
| | 4 | 897 | | resolvedPortal = seam.Portal; |
| | 4 | 898 | | reverse = seam.IsReverse; |
| | | 899 | | } |
| | 1179 | 900 | | resolvedPortal.TryResolveProfile( |
| | 1179 | 901 | | shape.Radius, |
| | 1179 | 902 | | shape.Height, |
| | 1179 | 903 | | out Vector3d first, |
| | 1179 | 904 | | out Vector3d second); |
| | 1179 | 905 | | sourcePortal = reverse ? second : first; |
| | 1179 | 906 | | targetPortal = reverse ? first : second; |
| | 1179 | 907 | | return NavigationGuideStatus.Success; |
| | | 908 | | } |
| | | 909 | | |
| | | 910 | | private static NavigationGuideStatus TrySampleDirectedLeg( |
| | | 911 | | Vector3d sourceFootAnchor, |
| | | 912 | | Vector3d sourcePortal, |
| | | 913 | | Vector3d targetPortal, |
| | | 914 | | Vector3d targetFootAnchor, |
| | | 915 | | Vector3d actualFootPosition, |
| | | 916 | | ref GuideSampleWorkMeter meter, |
| | | 917 | | out Vector3d heading) |
| | | 918 | | { |
| | 336 | 919 | | int traceIntervals = sourcePortal == targetPortal ? 2 : 3; |
| | 336 | 920 | | if (!meter.TryConsumeCursorLegScans(1) |
| | 336 | 921 | | || !meter.TryConsumeTraceIntervals(traceIntervals)) |
| | | 922 | | { |
| | 3 | 923 | | heading = Vector3d.Zero; |
| | 3 | 924 | | return NavigationGuideStatus.BudgetExceeded; |
| | | 925 | | } |
| | 333 | 926 | | return TrySampleDirectedLegUnchecked( |
| | 333 | 927 | | sourceFootAnchor, |
| | 333 | 928 | | sourcePortal, |
| | 333 | 929 | | targetPortal, |
| | 333 | 930 | | targetFootAnchor, |
| | 333 | 931 | | actualFootPosition, |
| | 333 | 932 | | out heading); |
| | | 933 | | } |
| | | 934 | | |
| | | 935 | | private static NavigationGuideStatus TrySampleDirectedLegUnchecked( |
| | | 936 | | Vector3d sourceFootAnchor, |
| | | 937 | | Vector3d sourcePortal, |
| | | 938 | | Vector3d targetPortal, |
| | | 939 | | Vector3d targetFootAnchor, |
| | | 940 | | Vector3d actualFootPosition, |
| | | 941 | | out Vector3d heading) |
| | | 942 | | { |
| | 333 | 943 | | NavigationGuideStatus status = HasReachedOrPassed( |
| | 333 | 944 | | sourceFootAnchor, |
| | 333 | 945 | | sourcePortal, |
| | 333 | 946 | | actualFootPosition, |
| | 333 | 947 | | out bool passedSourcePortal); |
| | 333 | 948 | | bool passedTargetPortal = false; |
| | 333 | 949 | | if (status == NavigationGuideStatus.Success && passedSourcePortal) |
| | | 950 | | { |
| | 2 | 951 | | status = HasReachedOrPassed( |
| | 2 | 952 | | sourcePortal, |
| | 2 | 953 | | targetPortal, |
| | 2 | 954 | | actualFootPosition, |
| | 2 | 955 | | out passedTargetPortal); |
| | | 956 | | } |
| | 333 | 957 | | if (status != NavigationGuideStatus.Success) |
| | 1 | 958 | | return FailDirectedLeg(status, out heading); |
| | 332 | 959 | | if (!passedSourcePortal) |
| | | 960 | | { |
| | 330 | 961 | | return TrySetHeadingUnchecked(actualFootPosition, sourcePortal, out heading); |
| | | 962 | | } |
| | 2 | 963 | | return TrySetHeadingUnchecked( |
| | 2 | 964 | | actualFootPosition, |
| | 2 | 965 | | passedTargetPortal ? targetFootAnchor : targetPortal, |
| | 2 | 966 | | out heading); |
| | | 967 | | } |
| | | 968 | | |
| | | 969 | | private static NavigationGuideStatus FailDirectedLeg( |
| | | 970 | | NavigationGuideStatus status, |
| | | 971 | | out Vector3d heading) |
| | | 972 | | { |
| | 1 | 973 | | heading = Vector3d.Zero; |
| | 1 | 974 | | return status; |
| | | 975 | | } |
| | | 976 | | |
| | | 977 | | private static NavigationGuideStatus TrySampleExplicit( |
| | | 978 | | NavigationWorldGraph graph, |
| | | 979 | | NavigationExplicitConnectionRecord record, |
| | | 980 | | NavigationCellAddress sourceAddress, |
| | | 981 | | NavigationNodeState sourceState, |
| | | 982 | | GridCellPrism sourcePrism, |
| | | 983 | | NavigationNodeState targetState, |
| | | 984 | | GridCellPrism targetPrism, |
| | | 985 | | bool sourceContains, |
| | | 986 | | bool targetContains, |
| | | 987 | | Vector3d actualFootPosition, |
| | | 988 | | KinematicBodyShape shape, |
| | | 989 | | bool targetIsDestination, |
| | | 990 | | Fixed64 arrivalRadius, |
| | | 991 | | ref GuideSampleWorkMeter meter, |
| | | 992 | | out NavigationCellAddress nextSourceAddress, |
| | | 993 | | out Vector3d heading) |
| | | 994 | | { |
| | 30 | 995 | | nextSourceAddress = sourceAddress; |
| | 30 | 996 | | heading = Vector3d.Zero; |
| | 30 | 997 | | NavigationConnection connection = record.Definition; |
| | 30 | 998 | | NavigationPagedSequence<GridNavigationPortal>.Enumerator portals = |
| | 30 | 999 | | record.NavigationPortals.GetEnumerator(); |
| | 30 | 1000 | | NavigationGuideStatus firstPortalStatus = TryReadExplicitPortal( |
| | 30 | 1001 | | ref portals, |
| | 30 | 1002 | | shape, |
| | 30 | 1003 | | ref meter, |
| | 30 | 1004 | | out GridNavigationPortal incomingPortal, |
| | 30 | 1005 | | out Vector3d incomingSourceAnchor, |
| | 30 | 1006 | | out Vector3d incomingTargetAnchor); |
| | 30 | 1007 | | if (firstPortalStatus != NavigationGuideStatus.Success) |
| | 1 | 1008 | | return firstPortalStatus; |
| | | 1009 | | |
| | 29 | 1010 | | bool zeroWitnessTargetBodyValid = connection.Witnesses.Count == 0 |
| | 29 | 1011 | | && targetContains |
| | 29 | 1012 | | && GridCellGeometry.IsNavigationBodyAnchorValid( |
| | 29 | 1013 | | targetPrism, |
| | 29 | 1014 | | actualFootPosition, |
| | 29 | 1015 | | shape.Radius, |
| | 29 | 1016 | | shape.Height, |
| | 29 | 1017 | | incomingPortal); |
| | 29 | 1018 | | if (targetIsDestination |
| | 29 | 1019 | | && zeroWitnessTargetBodyValid |
| | 29 | 1020 | | && IsWithinArrivalRadius(actualFootPosition, targetState.FootAnchor, arrivalRadius)) |
| | | 1021 | | { |
| | 1 | 1022 | | return NavigationGuideStatus.Success; |
| | | 1023 | | } |
| | | 1024 | | |
| | 28 | 1025 | | bool sourceBodyValid = sourceContains |
| | 28 | 1026 | | && GridCellGeometry.IsNavigationBodyAnchorValid( |
| | 28 | 1027 | | sourcePrism, |
| | 28 | 1028 | | actualFootPosition, |
| | 28 | 1029 | | shape.Radius, |
| | 28 | 1030 | | shape.Height, |
| | 28 | 1031 | | incomingPortal); |
| | 28 | 1032 | | if (sourceBodyValid && !zeroWitnessTargetBodyValid) |
| | | 1033 | | { |
| | 6 | 1034 | | NavigationGuideStatus progressStatus = HasReachedOrPassed( |
| | 6 | 1035 | | sourceState.FootAnchor, |
| | 6 | 1036 | | connection.EntryAnchor, |
| | 6 | 1037 | | actualFootPosition, |
| | 6 | 1038 | | out bool passedEntry); |
| | 6 | 1039 | | if (progressStatus != NavigationGuideStatus.Success) |
| | 1 | 1040 | | return progressStatus; |
| | 5 | 1041 | | if (!passedEntry) |
| | | 1042 | | { |
| | 2 | 1043 | | return TrySetHeading( |
| | 2 | 1044 | | actualFootPosition, |
| | 2 | 1045 | | connection.EntryAnchor, |
| | 2 | 1046 | | ref meter, |
| | 2 | 1047 | | out heading); |
| | | 1048 | | } |
| | 3 | 1049 | | if (connection.Witnesses.Count == 0) |
| | | 1050 | | { |
| | 2 | 1051 | | return TrySampleDirectedLeg( |
| | 2 | 1052 | | connection.EntryAnchor, |
| | 2 | 1053 | | incomingSourceAnchor, |
| | 2 | 1054 | | incomingTargetAnchor, |
| | 2 | 1055 | | connection.ExitAnchor, |
| | 2 | 1056 | | actualFootPosition, |
| | 2 | 1057 | | ref meter, |
| | 2 | 1058 | | out heading); |
| | | 1059 | | } |
| | 1 | 1060 | | NavigationCellAddress firstAddress = connection.Witnesses[0]; |
| | 1 | 1061 | | graph.TryGetNodeRef(firstAddress, out NavigationNodeRef firstRef); |
| | 1 | 1062 | | graph.TryGetNodeState(firstRef, out NavigationNodeState firstState); |
| | 1 | 1063 | | return TrySampleDirectedLeg( |
| | 1 | 1064 | | connection.EntryAnchor, |
| | 1 | 1065 | | incomingSourceAnchor, |
| | 1 | 1066 | | incomingTargetAnchor, |
| | 1 | 1067 | | firstState.FootAnchor, |
| | 1 | 1068 | | actualFootPosition, |
| | 1 | 1069 | | ref meter, |
| | 1 | 1070 | | out heading); |
| | | 1071 | | } |
| | | 1072 | | |
| | 82 | 1073 | | for (int i = 0; i < connection.Witnesses.Count; i++) |
| | | 1074 | | { |
| | 26 | 1075 | | NavigationGuideStatus outgoingPortalStatus = TryReadExplicitPortal( |
| | 26 | 1076 | | ref portals, |
| | 26 | 1077 | | shape, |
| | 26 | 1078 | | ref meter, |
| | 26 | 1079 | | out GridNavigationPortal outgoingPortal, |
| | 26 | 1080 | | out Vector3d outgoingSourceAnchor, |
| | 26 | 1081 | | out Vector3d outgoingTargetAnchor); |
| | 26 | 1082 | | if (outgoingPortalStatus != NavigationGuideStatus.Success) |
| | 1 | 1083 | | return outgoingPortalStatus; |
| | 25 | 1084 | | NavigationCellAddress witnessAddress = connection.Witnesses[i]; |
| | 25 | 1085 | | graph.TryGetSeamPrism(witnessAddress, out GridCellPrism witnessPrism); |
| | 25 | 1086 | | if (!meter.TryConsumePrismChecks(1)) |
| | 1 | 1087 | | return NavigationGuideStatus.BudgetExceeded; |
| | 24 | 1088 | | if (!witnessPrism.Contains(actualFootPosition)) |
| | | 1089 | | { |
| | 18 | 1090 | | incomingPortal = outgoingPortal; |
| | 18 | 1091 | | incomingTargetAnchor = outgoingTargetAnchor; |
| | 18 | 1092 | | continue; |
| | | 1093 | | } |
| | 6 | 1094 | | bool witnessBodyValid = GridCellGeometry.IsNavigationBodyAnchorValid( |
| | 6 | 1095 | | witnessPrism, |
| | 6 | 1096 | | actualFootPosition, |
| | 6 | 1097 | | shape.Radius, |
| | 6 | 1098 | | shape.Height, |
| | 6 | 1099 | | outgoingPortal) |
| | 6 | 1100 | | || GridCellGeometry.IsNavigationBodyAnchorValid( |
| | 6 | 1101 | | witnessPrism, |
| | 6 | 1102 | | actualFootPosition, |
| | 6 | 1103 | | shape.Radius, |
| | 6 | 1104 | | shape.Height, |
| | 6 | 1105 | | incomingPortal); |
| | 6 | 1106 | | if (!witnessBodyValid) |
| | | 1107 | | { |
| | 1 | 1108 | | incomingPortal = outgoingPortal; |
| | 1 | 1109 | | incomingTargetAnchor = outgoingTargetAnchor; |
| | 1 | 1110 | | continue; |
| | | 1111 | | } |
| | 5 | 1112 | | NavigationCellAddress nextAddress = i + 1 < connection.Witnesses.Count |
| | 5 | 1113 | | ? connection.Witnesses[i + 1] |
| | 5 | 1114 | | : record.Destination; |
| | | 1115 | | NavigationNodeState nextState; |
| | 5 | 1116 | | if (nextAddress == record.Destination) |
| | | 1117 | | { |
| | 1 | 1118 | | nextState = targetState; |
| | | 1119 | | } |
| | | 1120 | | else |
| | | 1121 | | { |
| | 4 | 1122 | | graph.TryGetNodeRef(nextAddress, out NavigationNodeRef nextRef); |
| | 4 | 1123 | | graph.TryGetNodeState(nextRef, out nextState); |
| | | 1124 | | } |
| | 5 | 1125 | | return TrySampleDirectedLeg( |
| | 5 | 1126 | | incomingTargetAnchor, |
| | 5 | 1127 | | outgoingSourceAnchor, |
| | 5 | 1128 | | outgoingTargetAnchor, |
| | 5 | 1129 | | nextAddress == record.Destination |
| | 5 | 1130 | | ? connection.ExitAnchor |
| | 5 | 1131 | | : nextState.FootAnchor, |
| | 5 | 1132 | | actualFootPosition, |
| | 5 | 1133 | | ref meter, |
| | 5 | 1134 | | out heading); |
| | | 1135 | | } |
| | | 1136 | | |
| | 15 | 1137 | | bool targetBodyValid = zeroWitnessTargetBodyValid |
| | 15 | 1138 | | || (connection.Witnesses.Count > 0 |
| | 15 | 1139 | | && targetContains |
| | 15 | 1140 | | && GridCellGeometry.IsNavigationBodyAnchorValid( |
| | 15 | 1141 | | targetPrism, |
| | 15 | 1142 | | actualFootPosition, |
| | 15 | 1143 | | shape.Radius, |
| | 15 | 1144 | | shape.Height, |
| | 15 | 1145 | | incomingPortal)); |
| | 15 | 1146 | | if (targetBodyValid) |
| | | 1147 | | { |
| | 9 | 1148 | | if (targetIsDestination |
| | 9 | 1149 | | && IsWithinArrivalRadius(actualFootPosition, targetState.FootAnchor, arrivalRadius)) |
| | | 1150 | | { |
| | 1 | 1151 | | return NavigationGuideStatus.Success; |
| | | 1152 | | } |
| | 8 | 1153 | | NavigationGuideStatus progressStatus = HasReachedOrPassed( |
| | 8 | 1154 | | incomingTargetAnchor, |
| | 8 | 1155 | | connection.ExitAnchor, |
| | 8 | 1156 | | actualFootPosition, |
| | 8 | 1157 | | out bool passedExit); |
| | 8 | 1158 | | if (progressStatus != NavigationGuideStatus.Success) |
| | 1 | 1159 | | return progressStatus; |
| | 7 | 1160 | | if (!passedExit) |
| | | 1161 | | { |
| | 2 | 1162 | | return TrySetHeading( |
| | 2 | 1163 | | actualFootPosition, |
| | 2 | 1164 | | connection.ExitAnchor, |
| | 2 | 1165 | | ref meter, |
| | 2 | 1166 | | out heading); |
| | | 1167 | | } |
| | 5 | 1168 | | progressStatus = HasReachedOrPassed( |
| | 5 | 1169 | | connection.ExitAnchor, |
| | 5 | 1170 | | targetState.FootAnchor, |
| | 5 | 1171 | | actualFootPosition, |
| | 5 | 1172 | | out bool passedTarget); |
| | 5 | 1173 | | if (progressStatus != NavigationGuideStatus.Success) |
| | 1 | 1174 | | return progressStatus; |
| | 4 | 1175 | | if (!passedTarget) |
| | | 1176 | | { |
| | 1 | 1177 | | return TrySetHeading( |
| | 1 | 1178 | | actualFootPosition, |
| | 1 | 1179 | | targetState.FootAnchor, |
| | 1 | 1180 | | ref meter, |
| | 1 | 1181 | | out heading); |
| | | 1182 | | } |
| | 3 | 1183 | | if (!meter.TryConsumeCursorRebases(1)) |
| | 1 | 1184 | | return NavigationGuideStatus.BudgetExceeded; |
| | 2 | 1185 | | nextSourceAddress = record.Destination; |
| | 2 | 1186 | | return NavigationGuideStatus.Success; |
| | | 1187 | | } |
| | 6 | 1188 | | return NavigationGuideStatus.LocalRecoveryRequired; |
| | | 1189 | | } |
| | | 1190 | | |
| | | 1191 | | private static NavigationGuideStatus TryReadExplicitPortal( |
| | | 1192 | | ref NavigationPagedSequence<GridNavigationPortal>.Enumerator portals, |
| | | 1193 | | KinematicBodyShape shape, |
| | | 1194 | | ref GuideSampleWorkMeter meter, |
| | | 1195 | | out GridNavigationPortal portal, |
| | | 1196 | | out Vector3d sourcePortalAnchor, |
| | | 1197 | | out Vector3d targetPortalAnchor) |
| | | 1198 | | { |
| | 56 | 1199 | | portal = default; |
| | 56 | 1200 | | sourcePortalAnchor = default; |
| | 56 | 1201 | | targetPortalAnchor = default; |
| | 56 | 1202 | | if (!meter.TryConsumePortalChecks(1)) |
| | 2 | 1203 | | return NavigationGuideStatus.BudgetExceeded; |
| | 54 | 1204 | | portals.MoveNext(); |
| | 54 | 1205 | | portal = portals.Current; |
| | 54 | 1206 | | portal.TryResolveProfile( |
| | 54 | 1207 | | shape.Radius, |
| | 54 | 1208 | | shape.Height, |
| | 54 | 1209 | | out sourcePortalAnchor, |
| | 54 | 1210 | | out targetPortalAnchor); |
| | 54 | 1211 | | return NavigationGuideStatus.Success; |
| | | 1212 | | } |
| | | 1213 | | |
| | | 1214 | | internal static NavigationGuideStatus TrySetHeading( |
| | | 1215 | | Vector3d actualFootPosition, |
| | | 1216 | | Vector3d target, |
| | | 1217 | | ref GuideSampleWorkMeter meter, |
| | | 1218 | | out Vector3d heading) |
| | | 1219 | | { |
| | 14 | 1220 | | if (!meter.TryConsumeCursorLegScans(1) |
| | 14 | 1221 | | || !meter.TryConsumeTraceIntervals(1)) |
| | | 1222 | | { |
| | 2 | 1223 | | heading = Vector3d.Zero; |
| | 2 | 1224 | | return NavigationGuideStatus.BudgetExceeded; |
| | | 1225 | | } |
| | 12 | 1226 | | return TrySetHeadingUnchecked(actualFootPosition, target, out heading); |
| | | 1227 | | } |
| | | 1228 | | |
| | | 1229 | | private static NavigationGuideStatus TrySetHeadingUnchecked( |
| | | 1230 | | Vector3d actualFootPosition, |
| | | 1231 | | Vector3d target, |
| | | 1232 | | out Vector3d heading) |
| | | 1233 | | { |
| | 724 | 1234 | | heading = Vector3d.Zero; |
| | 724 | 1235 | | if (!Vector3d.TrySubtract(target, actualFootPosition, out Vector3d delta)) |
| | 1 | 1236 | | return NavigationGuideStatus.CostOverflow; |
| | 723 | 1237 | | if (delta != Vector3d.Zero) |
| | 714 | 1238 | | heading = delta.Normalized; |
| | 723 | 1239 | | return NavigationGuideStatus.Success; |
| | | 1240 | | } |
| | | 1241 | | |
| | | 1242 | | internal static NavigationGuideStatus HasReachedOrPassed( |
| | | 1243 | | Vector3d start, |
| | | 1244 | | Vector3d end, |
| | | 1245 | | Vector3d actual, |
| | | 1246 | | out bool passed) |
| | | 1247 | | { |
| | 369 | 1248 | | passed = false; |
| | 369 | 1249 | | if (start == end) |
| | | 1250 | | { |
| | 4 | 1251 | | passed = true; |
| | 4 | 1252 | | return NavigationGuideStatus.Success; |
| | | 1253 | | } |
| | 365 | 1254 | | if (!Vector3d.TrySubtract(end, start, out Vector3d direction) |
| | 365 | 1255 | | || !Vector3d.TrySubtract(actual, end, out Vector3d beyond) |
| | 365 | 1256 | | || !Vector3d.TryDot(beyond, direction, out Fixed64 projection)) |
| | | 1257 | | { |
| | 8 | 1258 | | return NavigationGuideStatus.CostOverflow; |
| | | 1259 | | } |
| | 357 | 1260 | | passed = projection >= Fixed64.Zero; |
| | 357 | 1261 | | return NavigationGuideStatus.Success; |
| | | 1262 | | } |
| | | 1263 | | |
| | | 1264 | | private static NavigationGuideStatus TryRebase( |
| | | 1265 | | GridWorld world, |
| | | 1266 | | NavigationWorldGraph graph, |
| | | 1267 | | NavigationFlowFieldPayload payload, |
| | | 1268 | | TraversalMedium medium, |
| | | 1269 | | Vector3d actualFootPosition, |
| | | 1270 | | ref GuideSampleWorkMeter meter, |
| | | 1271 | | GridCoveredAddressCursor cursor, |
| | | 1272 | | GridCoveredAddressGeneration[] generations, |
| | | 1273 | | GridCoveredAddress[] output, |
| | | 1274 | | out NavigationCellAddress rebased) |
| | | 1275 | | { |
| | 857 | 1276 | | rebased = default; |
| | 857 | 1277 | | if (!meter.TryConsumeLocalRecoveryAttempts(1) |
| | 857 | 1278 | | || !meter.TryConsumeCursorRebases(1)) |
| | | 1279 | | { |
| | 7 | 1280 | | return NavigationGuideStatus.BudgetExceeded; |
| | | 1281 | | } |
| | 3398 | 1282 | | for (int i = 0; i < graph.MapCount; i++) |
| | | 1283 | | { |
| | 850 | 1284 | | if (!meter.TryConsumeCurrentNodeLookupProbes(1)) |
| | 1 | 1285 | | return NavigationGuideStatus.BudgetExceeded; |
| | 849 | 1286 | | bool foundGeneration = graph.TryGetCoveredAddressGeneration( |
| | 849 | 1287 | | i, |
| | 849 | 1288 | | out _, |
| | 849 | 1289 | | out generations[i]); |
| | | 1290 | | System.Diagnostics.Debug.Assert( |
| | | 1291 | | foundGeneration, |
| | | 1292 | | "published graph instances are materialized and indexed one-to-one"); |
| | | 1293 | | } |
| | 849 | 1294 | | bool began = world.TryBeginCoveredAddresses( |
| | 849 | 1295 | | cursor, |
| | 849 | 1296 | | actualFootPosition, |
| | 849 | 1297 | | actualFootPosition, |
| | 849 | 1298 | | graph.MapCount); |
| | | 1299 | | System.Diagnostics.Debug.Assert(began); |
| | | 1300 | | |
| | 849 | 1301 | | int inputOrdinal = 0; |
| | 849 | 1302 | | bool hasCandidate = false; |
| | 849 | 1303 | | Fixed64 bestDistance = Fixed64.Zero; |
| | 849 | 1304 | | NavigationCellAddress best = default; |
| | | 1305 | | while (true) |
| | | 1306 | | { |
| | 3994 | 1307 | | if (inputOrdinal < graph.MapCount) |
| | | 1308 | | { |
| | 849 | 1309 | | int available = meter.GetCurrentNodeLookupAllowance(); |
| | 849 | 1310 | | if (available == 0) |
| | 1 | 1311 | | return NavigationGuideStatus.BudgetExceeded; |
| | 848 | 1312 | | GridCoveredAddressCursorStatus bindStatus = world.AdvanceCoveredAddresses( |
| | 848 | 1313 | | cursor, |
| | 848 | 1314 | | generations.AsSpan(inputOrdinal, graph.MapCount - inputOrdinal), |
| | 848 | 1315 | | output, |
| | 848 | 1316 | | lookupProbeLimit: available, |
| | 848 | 1317 | | addressProbeLimit: 0, |
| | 848 | 1318 | | outputLimit: 0, |
| | 848 | 1319 | | out int lookupProbes, |
| | 848 | 1320 | | out int addressProbes, |
| | 848 | 1321 | | out int inputsConsumed, |
| | 848 | 1322 | | out int outputCount); |
| | 848 | 1323 | | meter.TryConsumeCurrentNodeLookupProbes( |
| | 848 | 1324 | | checked(lookupProbes + addressProbes + outputCount)); |
| | 848 | 1325 | | inputOrdinal += inputsConsumed; |
| | 848 | 1326 | | if (bindStatus == GridCoveredAddressCursorStatus.Stale) |
| | 1 | 1327 | | return NavigationGuideStatus.Stale; |
| | | 1328 | | continue; |
| | | 1329 | | } |
| | | 1330 | | |
| | 3145 | 1331 | | int outputBudget = meter.GetCurrentNodeLookupAllowance() > 0 ? 1 : 0; |
| | 3145 | 1332 | | GridCoveredAddressCursorStatus flushStatus = world.AdvanceCoveredAddresses( |
| | 3145 | 1333 | | cursor, |
| | 3145 | 1334 | | ReadOnlySpan<GridCoveredAddressGeneration>.Empty, |
| | 3145 | 1335 | | output, |
| | 3145 | 1336 | | lookupProbeLimit: 0, |
| | 3145 | 1337 | | addressProbeLimit: 0, |
| | 3145 | 1338 | | outputLimit: outputBudget, |
| | 3145 | 1339 | | out int flushLookups, |
| | 3145 | 1340 | | out int flushAddresses, |
| | 3145 | 1341 | | out _, |
| | 3145 | 1342 | | out int flushed); |
| | 3145 | 1343 | | meter.TryConsumeCurrentNodeLookupProbes( |
| | 3145 | 1344 | | checked(flushLookups + flushAddresses + flushed)); |
| | 3145 | 1345 | | if (flushed != 0) |
| | | 1346 | | { |
| | | 1347 | | System.Diagnostics.Debug.Assert( |
| | | 1348 | | flushStatus != GridCoveredAddressCursorStatus.Stale, |
| | | 1349 | | "a stale covered-address cursor cannot publish output"); |
| | 1150 | 1350 | | NavigationGuideStatus candidateStatus = ConsiderCandidate( |
| | 1150 | 1351 | | graph, |
| | 1150 | 1352 | | payload, |
| | 1150 | 1353 | | medium, |
| | 1150 | 1354 | | actualFootPosition, |
| | 1150 | 1355 | | output[0], |
| | 1150 | 1356 | | ref meter, |
| | 1150 | 1357 | | ref hasCandidate, |
| | 1150 | 1358 | | ref bestDistance, |
| | 1150 | 1359 | | ref best); |
| | 1150 | 1360 | | if (candidateStatus != NavigationGuideStatus.Success) |
| | 2 | 1361 | | return candidateStatus; |
| | | 1362 | | continue; |
| | | 1363 | | } |
| | 1995 | 1364 | | if (TryResolveRebaseCursorStatus( |
| | 1995 | 1365 | | flushStatus, |
| | 1995 | 1366 | | hasCandidate, |
| | 1995 | 1367 | | best, |
| | 1995 | 1368 | | out NavigationGuideStatus flushResult, |
| | 1995 | 1369 | | out rebased)) |
| | | 1370 | | { |
| | 842 | 1371 | | return flushResult; |
| | | 1372 | | } |
| | | 1373 | | |
| | 1153 | 1374 | | int addressBudget = meter.GetCurrentNodeLookupAllowance(); |
| | 1153 | 1375 | | if (addressBudget == 0) |
| | 1 | 1376 | | return NavigationGuideStatus.BudgetExceeded; |
| | 1152 | 1377 | | GridCoveredAddressCursorStatus addressStatus = world.AdvanceCoveredAddresses( |
| | 1152 | 1378 | | cursor, |
| | 1152 | 1379 | | ReadOnlySpan<GridCoveredAddressGeneration>.Empty, |
| | 1152 | 1380 | | output, |
| | 1152 | 1381 | | lookupProbeLimit: 0, |
| | 1152 | 1382 | | addressProbeLimit: addressBudget, |
| | 1152 | 1383 | | outputLimit: 0, |
| | 1152 | 1384 | | out int addressLookups, |
| | 1152 | 1385 | | out int enumeratedAddresses, |
| | 1152 | 1386 | | out _, |
| | 1152 | 1387 | | out int addressOutputs); |
| | 1152 | 1388 | | meter.TryConsumeCurrentNodeLookupProbes( |
| | 1152 | 1389 | | checked(addressLookups + enumeratedAddresses + addressOutputs)); |
| | 1152 | 1390 | | if (TryResolveRebaseCursorStatus( |
| | 1152 | 1391 | | addressStatus, |
| | 1152 | 1392 | | hasCandidate, |
| | 1152 | 1393 | | best, |
| | 1152 | 1394 | | out NavigationGuideStatus addressResult, |
| | 1152 | 1395 | | out rebased)) |
| | | 1396 | | { |
| | 2 | 1397 | | return addressResult; |
| | | 1398 | | } |
| | | 1399 | | } |
| | | 1400 | | } |
| | | 1401 | | |
| | | 1402 | | internal static bool TryResolveRebaseCursorStatus( |
| | | 1403 | | GridCoveredAddressCursorStatus cursorStatus, |
| | | 1404 | | bool hasCandidate, |
| | | 1405 | | NavigationCellAddress best, |
| | | 1406 | | out NavigationGuideStatus status, |
| | | 1407 | | out NavigationCellAddress rebased) |
| | | 1408 | | { |
| | 3151 | 1409 | | if (cursorStatus == GridCoveredAddressCursorStatus.Stale) |
| | | 1410 | | { |
| | 1 | 1411 | | status = NavigationGuideStatus.Stale; |
| | 1 | 1412 | | rebased = default; |
| | 1 | 1413 | | return true; |
| | | 1414 | | } |
| | 3150 | 1415 | | if (cursorStatus == GridCoveredAddressCursorStatus.Complete) |
| | | 1416 | | { |
| | 846 | 1417 | | status = hasCandidate |
| | 846 | 1418 | | ? NavigationGuideStatus.Success |
| | 846 | 1419 | | : NavigationGuideStatus.LocalRecoveryRequired; |
| | 846 | 1420 | | rebased = best; |
| | 846 | 1421 | | return true; |
| | | 1422 | | } |
| | 2304 | 1423 | | status = NavigationGuideStatus.Success; |
| | 2304 | 1424 | | rebased = default; |
| | 2304 | 1425 | | return false; |
| | | 1426 | | } |
| | | 1427 | | |
| | | 1428 | | private static NavigationGuideStatus ConsiderCandidate( |
| | | 1429 | | NavigationWorldGraph graph, |
| | | 1430 | | NavigationFlowFieldPayload payload, |
| | | 1431 | | TraversalMedium medium, |
| | | 1432 | | Vector3d actualFootPosition, |
| | | 1433 | | GridCoveredAddress candidate, |
| | | 1434 | | ref GuideSampleWorkMeter meter, |
| | | 1435 | | ref bool hasCandidate, |
| | | 1436 | | ref Fixed64 bestDistance, |
| | | 1437 | | ref NavigationCellAddress best) |
| | | 1438 | | { |
| | | 1439 | | // Covered generations originate from this graph's configuration index. |
| | 1150 | 1440 | | graph.TryGetMapId(candidate.ConfigurationKey, out string mapId); |
| | 1150 | 1441 | | var address = new NavigationCellAddress(mapId, candidate.VoxelIndex); |
| | 1150 | 1442 | | return ConsiderCandidateAddress( |
| | 1150 | 1443 | | graph, |
| | 1150 | 1444 | | payload, |
| | 1150 | 1445 | | medium, |
| | 1150 | 1446 | | actualFootPosition, |
| | 1150 | 1447 | | address, |
| | 1150 | 1448 | | ref meter, |
| | 1150 | 1449 | | ref hasCandidate, |
| | 1150 | 1450 | | ref bestDistance, |
| | 1150 | 1451 | | ref best); |
| | | 1452 | | } |
| | | 1453 | | |
| | | 1454 | | internal static NavigationGuideStatus ConsiderCandidateAddress( |
| | | 1455 | | NavigationWorldGraph graph, |
| | | 1456 | | NavigationFlowFieldPayload payload, |
| | | 1457 | | TraversalMedium medium, |
| | | 1458 | | Vector3d actualFootPosition, |
| | | 1459 | | NavigationCellAddress address, |
| | | 1460 | | ref GuideSampleWorkMeter meter, |
| | | 1461 | | ref bool hasCandidate, |
| | | 1462 | | ref Fixed64 bestDistance, |
| | | 1463 | | ref NavigationCellAddress best) |
| | | 1464 | | { |
| | 1151 | 1465 | | if (!meter.TryConsumePrismChecks(1)) |
| | 1 | 1466 | | return NavigationGuideStatus.BudgetExceeded; |
| | 1150 | 1467 | | bool foundPrism = graph.TryGetSeamPrism(address, out GridCellPrism prism); |
| | | 1468 | | System.Diagnostics.Debug.Assert(foundPrism, |
| | | 1469 | | "covered-address candidates retain their graph-owned configuration prism"); |
| | 1150 | 1470 | | if (!prism.Contains(actualFootPosition) |
| | 1150 | 1471 | | || !graph.TryGetNodeRef(address, out NavigationNodeRef node) |
| | 1150 | 1472 | | || !graph.TryGetNodeState(node, medium, out NavigationNodeState state) |
| | 1150 | 1473 | | || !state.IsPresent) |
| | | 1474 | | { |
| | 595 | 1475 | | return NavigationGuideStatus.Success; |
| | | 1476 | | } |
| | 555 | 1477 | | NavigationFlowNodeLookupStatus lookup = TryGetNode( |
| | 555 | 1478 | | payload, |
| | 555 | 1479 | | address, |
| | 555 | 1480 | | medium, |
| | 555 | 1481 | | ref meter, |
| | 555 | 1482 | | out _); |
| | 555 | 1483 | | if (lookup != NavigationFlowNodeLookupStatus.Success) |
| | | 1484 | | { |
| | 1 | 1485 | | return MapNodeLookupStatus(lookup, required: false); |
| | | 1486 | | } |
| | 554 | 1487 | | if (!Vector3d.TryGetDistance( |
| | 554 | 1488 | | actualFootPosition, |
| | 554 | 1489 | | state.FootAnchor, |
| | 554 | 1490 | | out Fixed64 distance)) |
| | | 1491 | | { |
| | 1 | 1492 | | return NavigationGuideStatus.CostOverflow; |
| | | 1493 | | } |
| | 553 | 1494 | | SelectNearestCandidate( |
| | 553 | 1495 | | distance, |
| | 553 | 1496 | | address, |
| | 553 | 1497 | | ref hasCandidate, |
| | 553 | 1498 | | ref bestDistance, |
| | 553 | 1499 | | ref best); |
| | 553 | 1500 | | return NavigationGuideStatus.Success; |
| | | 1501 | | } |
| | | 1502 | | |
| | | 1503 | | internal static void SelectNearestCandidate( |
| | | 1504 | | Fixed64 candidateDistance, |
| | | 1505 | | NavigationCellAddress candidate, |
| | | 1506 | | ref bool hasCandidate, |
| | | 1507 | | ref Fixed64 bestDistance, |
| | | 1508 | | ref NavigationCellAddress best) |
| | | 1509 | | { |
| | 559 | 1510 | | if (!hasCandidate |
| | 559 | 1511 | | || candidateDistance < bestDistance |
| | 559 | 1512 | | || (candidateDistance == bestDistance && candidate.CompareTo(best) < 0)) |
| | | 1513 | | { |
| | 556 | 1514 | | hasCandidate = true; |
| | 556 | 1515 | | bestDistance = candidateDistance; |
| | 556 | 1516 | | best = candidate; |
| | | 1517 | | } |
| | 559 | 1518 | | } |
| | | 1519 | | } |