TDME2  1.9.200
PathFinding.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <map>
5 #include <memory>
6 #include <stack>
7 #include <string>
8 #include <unordered_map>
9 #include <unordered_set>
10 #include <vector>
11 
12 #include <tdme/tdme.h>
17 #include <tdme/engine/Texture.h>
18 #include <tdme/engine/Transform.h>
19 #include <tdme/math/Math.h>
20 #include <tdme/math/Vector3.h>
21 #include <tdme/utilities/Console.h>
22 #include <tdme/utilities/Float.h>
24 #include <tdme/utilities/Pool.h>
25 #include <tdme/utilities/Terrain.h>
26 #include <tdme/utilities/Time.h>
27 
28 using std::make_unique;
29 using std::map;
30 using std::reverse;
31 using std::stack;
32 using std::string;
33 using std::to_string;
34 using std::unique_ptr;
35 using std::unordered_map;
36 using std::unordered_set;
37 using std::vector;
38 
45 using tdme::math::Math;
53 
55 
56 PathFinding::PathFinding(
57  World* world,
58  bool sloping,
59  int stepsMax,
60  float actorHeight,
61  float stepSize,
62  float stepSizeLast,
63  float actorStepUpMax,
64  uint16_t skipOnCollisionTypeIds,
65  int maxTries,
66  float flowMapStepSize,
67  float flowMapScaleActorBoundingVolumes
68  ) {
69  this->world = world;
70  this->sloping = sloping;
71  this->stepsMax = stepsMax;
72  this->actorHeight = actorHeight;
73  this->stepSize = stepSize;
74  this->stepSizeLast = stepSizeLast;
75  this->actorStepUpMax = actorStepUpMax;
76  this->skipOnCollisionTypeIds = skipOnCollisionTypeIds;
77  this->maxTries = maxTries;
78  this->collisionTypeIds = 0;
79  this->flowMapStepSize = flowMapStepSize;
80  this->flowMapScaleActorBoundingVolumes = flowMapScaleActorBoundingVolumes;
81 }
82 
84  if (navigationMap != nullptr) navigationMap->releaseReference();
85 }
86 
87 bool PathFinding::isWalkableInternal(float x, float y, float z, float& height, float stepSize, float scaleActorBoundingVolumes, bool flowMapRequest, PathFindingCustomTest* customTest, uint16_t collisionTypeIds, bool ignoreStepUpMax) {
88  // navigation map
89  if (navigationMap != nullptr &&
90  navigationMap->getTextureData()->get(static_cast<int>(((z + Terrain::STEP_SIZE / 2.0f) * 2.0f) / Terrain::STEP_SIZE) * static_cast<int>(navigationMap->getTextureWidth()) * 3 + static_cast<int>(((x + Terrain::STEP_SIZE / 2.0f) * 2.0f) / Terrain::STEP_SIZE) * 3) == 255) {
91  return false;
92  }
93  //
94  tuple<uint8_t, uint8_t, int, int, int, uint16_t, bool> cacheId;
95  if (flowMapRequest == true) {
96  cacheId = tuple<uint8_t, uint8_t, int, int, int, uint16_t, bool> {
97  static_cast<uint8_t>(stepSize * 10.0f),
98  static_cast<uint8_t>(scaleActorBoundingVolumes * 10.0f),
99  static_cast<int>(Math::ceil(x / stepSize)),
100  static_cast<int>(Math::ceil(y / 0.1f)),
101  static_cast<int>(Math::ceil(z / stepSize)),
103  ignoreStepUpMax
104  };
105  auto walkableCacheIt = walkableCache.find(cacheId);
106  if (walkableCacheIt != walkableCache.end()) {
107  height = walkableCacheIt->second;
108  return height > -10000.0f;
109  }
110  }
111  auto walkable = isWalkable(x, y, z, height, stepSize, scaleActorBoundingVolumes, flowMapRequest, collisionTypeIds, ignoreStepUpMax);
112  if (flowMapRequest == true) walkableCache[cacheId] = walkable == false?-10000.0f:height;
113  if (walkable == false) return false;
114  return customTest == nullptr || customTest->isWalkable(this, x, height, z) == true;
115 }
116 
117 bool PathFinding::isSlopeWalkableInternal(float x, float y, float z, float successorX, float successorY, float successorZ, float stepSize, float scaleActorBoundingVolumes, bool flowMapRequest, PathFindingCustomTest* customTest, uint16_t collisionTypeIds) {
118  // navigation map
119  if (navigationMap != nullptr &&
120  navigationMap->getTextureData()->get(static_cast<int>(((z + Terrain::STEP_SIZE / 2.0f) * 2.0f) / Terrain::STEP_SIZE) * static_cast<int>(navigationMap->getTextureWidth()) * 3 + static_cast<int>(((x + Terrain::STEP_SIZE / 2.0f) * 2.0f) / Terrain::STEP_SIZE) * 3) == 255) {
121  return false;
122  }
123 
124  //
125  float slopeAngle = 0.0f;
126 
127  // slope angle and center
128  auto toVector = Vector3(successorX, y, successorZ);
129  auto fromVector = Vector3(x, y, z);
130  auto axis = toVector.clone().sub(fromVector);
131  auto center = axis.clone().scale(0.5f).add(fromVector).setY(y + 0.1f);
132  axis.normalize();
133  slopeAngle = Vector3::computeAngle(
134  axis,
135  Vector3(0.0f, 0.0f, -1.0f),
136  Vector3(0.0f, 1.0f, 0.0f)
137  );
138 
139  tuple<uint8_t, uint8_t, int, int, int, uint16_t, int16_t> cacheId;
140  if (flowMapRequest == true) {
141  cacheId = tuple<uint8_t, uint8_t, int, int, int, uint16_t, int16_t> {
142  static_cast<uint8_t>(stepSize * 10.0f),
143  static_cast<uint8_t>(scaleActorBoundingVolumes * 10.0f),
144  static_cast<int>(Math::ceil(x / stepSize)),
145  static_cast<int>(Math::ceil(y / 0.1f)),
146  static_cast<int>(Math::ceil(z / stepSize)),
148  static_cast<int16_t>(slopeAngle),
149  };
150  auto walkableCacheIt = walkableCache.find(cacheId);
151  if (walkableCacheIt != walkableCache.end()) {
152  auto height = walkableCacheIt->second;
153  return true;
154  }
155  }
156 
157  // set up transform
158  Transform slopeTestTransform;
159  slopeTestTransform.setTranslation(center);
160  slopeTestTransform.addRotation(Vector3(0.0f, 1.0f, 0.0f), slopeAngle);
161  slopeTestTransform.update();
162 
163  // update rigid body
164  auto actorSlopeTestCollisionBody = world->getBody("tdme.pathfinding.actor.slopetest");
165  actorSlopeTestCollisionBody->setTransform(slopeTestTransform);
166 
167  // check if actor collides with world
168  vector<Body*> collidedRigidBodies;
169  auto walkable = world->doesCollideWith(collisionTypeIds == 0?this->collisionTypeIds:collisionTypeIds, actorSlopeTestCollisionBody, collidedRigidBodies) == false;
170  if (flowMapRequest == true) walkableCache[cacheId] = walkable == false?-10000.0f:0.0f;
171  return walkable;
172 }
173 
174 bool PathFinding::isWalkable(float x, float y, float z, float& height, float stepSize, float scaleActorBoundingVolumes, bool flowMapRequest, uint16_t collisionTypeIds, bool ignoreStepUpMax) {
175  // determine y height of ground plate of actor bounding volume
176  float _z = z - stepSize / 2.0f;
177  height = -10000.0f;
178  Vector3 actorPosition;
179  for (auto i = 0; i <= 4; i++) {
180  float _x = x - stepSize / 2.0f;
181  for (auto j = 0; j <= 4; j++) {
182  Vector3 actorPositionCandidate;
183  auto body = world->determineHeight(
184  collisionTypeIds == 0?this->collisionTypeIds:collisionTypeIds,
185  ignoreStepUpMax == true?10000.0f:actorStepUpMax,
186  actorPositionCandidate.set(_x, y, _z),
187  actorPosition,
188  -10000.0f,
189  ignoreStepUpMax == true?10000.0f:y + actorStepUpMax + 0.2f
190  );
191  if (body == nullptr || ((body->getCollisionTypeId() & skipOnCollisionTypeIds) != 0)) {
192  return false;
193  }
194  if (actorPosition.getY() > height) height = actorPosition.getY();
195  _x+= stepSize / 4.0f;
196  }
197  _z+= stepSize / 4.0f;
198  }
199 
200  // set up transform
201  Transform actorTransform;
202  actorTransform.setTranslation(Vector3(x, ignoreStepUpMax == true?height + 0.2f:Math::min(y + actorStepUpMax, height + 0.2f), z));
203  actorTransform.update();
204 
205  // update rigid body
206  auto actorCollisionBody = world->getBody("tdme.pathfinding.actor");
207  actorCollisionBody->setTransform(actorTransform);
208 
209  // check if actor collides with world
210  vector<Body*> collidedRigidBodies;
211  auto collision = world->doesCollideWith(collisionTypeIds == 0?this->collisionTypeIds:collisionTypeIds, actorCollisionBody, collidedRigidBodies);
212  return collision == false;
213 }
214 
215 void PathFinding::step(PathFindingNode* node, float stepSize, float scaleActorBoundingVolumes, const unordered_set<tuple<int, int, int>, PathFindingNodeId_Hash>* nodesToTestPtr, bool flowMapRequest, PathFindingCustomTest* customTest) {
216  auto nodeId = node->id;
217 
218  // Find valid successors
219  stack<PathFindingNode*> successorNodes;
220  for (auto z = -1; z <= 1; z++)
221  for (auto x = -1; x <= 1; x++)
222  if ((z != 0 || x != 0) &&
223  (sloping == true ||
224  (Math::abs(x) == 1 && Math::abs(z) == 1) == false)) {
225  auto successorX = static_cast<float>(x) * stepSize + (nodesToTestPtr != nullptr?static_cast<float>(node->x) * stepSize:node->position.getX());
226  auto successorZ = static_cast<float>(z) * stepSize + (nodesToTestPtr != nullptr?static_cast<float>(node->z) * stepSize:node->position.getZ());
227  if (nodesToTestPtr != nullptr) {
228  auto successorNodeId = toIdInt(
229  node->x + x,
230  0,
231  node->z + z
232  );
233  if (nodesToTestPtr->find(successorNodeId) == nodesToTestPtr->end()) continue;
234  }
235  auto slopeWalkable = Math::abs(x) == 1 && Math::abs(z) == 1?isSlopeWalkableInternal(node->position.getX(), node->position.getY(), node->position.getZ(), successorX, node->position.getY(), successorZ, stepSize, scaleActorBoundingVolumes, flowMapRequest, customTest):true;
236  //
237  float yHeight;
238  // first node or walkable?
239  if (slopeWalkable == true && isWalkableInternal(successorX, node->position.getY(), successorZ, yHeight, stepSize, scaleActorBoundingVolumes, flowMapRequest, customTest) == true) {
240  // check if successor node equals previous node / node
241  if (equals(node, successorX, yHeight, successorZ)) {
242  continue;
243  }
244  // Add the node to the available sucessorNodes
245  auto successorNode = pathFindingNodesPool.allocate();
246  successorNode->hasPreviousNode = false;
247  successorNode->position = Vector3(successorX, yHeight, successorZ);
248  successorNode->costsAll = 0.0f;
249  successorNode->costsReachPoint = 0.0f;
250  successorNode->costsEstimated = 0.0f;
251  successorNode->x = node->x + x;
252  successorNode->y = flowMapRequest == true?0:getIntegerPositionComponent(successorNode->position.getY(), 0.1f);
253  successorNode->z = node->z + z;
254  successorNode->id = toIdInt(
255  successorNode->x,
256  successorNode->y,
257  successorNode->z
258  );
259  successorNodes.push(successorNode);
260  }
261  }
262 
263  // Check successor nodes
264  while (successorNodes.empty() == false) {
265  auto successorNode = successorNodes.top();
266 
267  // Compute successor node's costs by costs to reach nodes point and the computed distance from node to successor node
268  float successorCostsReachPoint = node->costsReachPoint + computeDistance(successorNode, node);
269 
270  // Find sucessor node in open nodes list
271  PathFindingNode* openListNode = nullptr;
272  auto openListNodeIt = openNodes.find(successorNode->id);
273  if (openListNodeIt != openNodes.end()) {
274  openListNode = openListNodeIt->second;
275  }
276 
277  // found it in open nodes list
278  if (openListNode != nullptr) {
279  // is the node from open nodes less expensive, discard successor node
280  if (openListNode->costsReachPoint <= successorCostsReachPoint) {
281  // remove it from stack
282  pathFindingNodesPool.release(successorNode);
283  successorNodes.pop();
284  // discard it
285  continue;
286  }
287  }
288 
289  // Find successor node in closed nodes list
290  PathFindingNode* closedListNode = nullptr;
291  auto closedListNodeIt = closedNodes.find(successorNode->id);
292  if (closedListNodeIt != closedNodes.end()) {
293  closedListNode = closedListNodeIt->second;
294  }
295 
296  // found it in closed nodes list
297  if (closedListNode != nullptr) {
298  // is the node from closed nodes list less expensive, discard successor node
299  if (closedListNode->costsReachPoint <= successorCostsReachPoint) {
300  // remove it from stack
301  pathFindingNodesPool.release(successorNode);
302  successorNodes.pop();
303  // discard it
304  continue;
305  }
306  }
307 
308  // successor node is the node with least cost to this point
309  successorNode->hasPreviousNode = true;
310  successorNode->previousNodeId = nodeId;
311  successorNode->costsReachPoint = successorCostsReachPoint;
312  successorNode->costsEstimated = computeDistanceToEnd(successorNode);
313  successorNode->costsAll = successorNode->costsReachPoint + successorNode->costsEstimated;
314 
315  // remove found node from open nodes list, since it was less optimal
316  if (openListNode != nullptr) {
317  // remove open list node
318  pathFindingNodesPool.release(openListNode);
319  openNodes.erase(openListNodeIt);
320  }
321 
322  // remove found node from closed nodes list, since it was less optimal
323  if (closedListNode != nullptr) {
324  pathFindingNodesPool.release(closedListNode);
325  closedNodes.erase(closedListNodeIt);
326  }
327 
328  // add successor node to open nodes list, as we might want to check its successors to find a path to the end
329  openNodes[successorNode->id] = successorNode;
330 
331  // remove it from stack
332  successorNodes.pop();
333  }
334 
335  // add node to closed nodes list, as we checked its successors
336  closedNodes[nodeId] = node;
337 
338  // remove node from open nodes, as we checked its successors
339  openNodes.erase(nodeId);
340 }
341 
343  const Vector3& startPosition,
344  const Vector3& endPosition,
345  float stepSize,
346  float scaleActorBoundingVolumes,
347  const uint16_t collisionTypeIds,
348  vector<Vector3>& path,
349  int alternativeEndSteps,
350  int maxTriesOverride,
351  PathFindingCustomTest* customTest) {
352  // clear path
353  path.clear();
354 
355  //
356  auto now = Time::getCurrentMillis();
357 
358  //
359  auto currentMaxTries = maxTriesOverride == -1?this->maxTries:maxTriesOverride;
360 
361  // initialize custom test
362  if (customTest != nullptr) customTest->initialize();
363 
364  //
365  this->collisionTypeIds = collisionTypeIds;
366 
367  // init bounding volume, transform, collision body
368  auto actorBoundingVolume = make_unique<OrientedBoundingBox>(
369  Vector3(0.0f, actorHeight / 2.0f, 0.0f),
370  OrientedBoundingBox::AABB_AXIS_X,
371  OrientedBoundingBox::AABB_AXIS_Y,
372  OrientedBoundingBox::AABB_AXIS_Z,
373  Vector3(stepSize * scaleActorBoundingVolumes, actorHeight / 2.0f, stepSize * scaleActorBoundingVolumes)
374  );
375  // set up transform
376  Transform actorTransform;
377  actorTransform.setTranslation(startPosition);
378  actorTransform.update();
379  world->addDynamicCollisionBody("tdme.pathfinding.actor", Body::COLLISION_TYPEID_RESERVED, true, actorTransform, { actorBoundingVolume.get() });
380 
381  // init bounding volume for slope testcollision body
382  auto actorBoundingVolumeSlopeTest = make_unique<OrientedBoundingBox>(
383  Vector3(0.0f, actorHeight / 2.0f, 0.0f),
384  OrientedBoundingBox::AABB_AXIS_X,
385  OrientedBoundingBox::AABB_AXIS_Y,
386  OrientedBoundingBox::AABB_AXIS_Z,
387  Vector3(stepSize * scaleActorBoundingVolumes * 2.5f, actorHeight / 2.0f, stepSize * scaleActorBoundingVolumes * 2.5f)
388  );
389  world->addDynamicCollisionBody("tdme.pathfinding.actor.slopetest", Body::COLLISION_TYPEID_RESERVED, true, actorTransform, { actorBoundingVolumeSlopeTest.get() });
390 
391  //
392  bool success = false;
393 
394  // equal start and end position?
395  if (endPosition.clone().sub(startPosition).computeLengthSquared() < Math::square(0.1f)) {
396  if (VERBOSE == true) Console::println("PathFinding::findPath(): start position == end position! Exiting!");
397  path.push_back(startPosition);
398  path.push_back(endPosition);
399  success = true;
400  } else
401  // nearby start and end position?
402  if (startPosition.clone().sub(endPosition).computeLengthSquared() < stepSizeLast * stepSizeLast + stepSizeLast * stepSizeLast + 0.1f) {
403  if (VERBOSE == true) Console::println("PathFinding::findPath(): end - start position < stepSizeLast! Exiting!");
404  path.push_back(startPosition);
405  float endYHeight = endPosition.getY();
406  if (isWalkableInternal(
407  endPosition.getX(),
408  endPosition.getY(),
409  endPosition.getZ(),
410  endYHeight,
411  stepSize,
412  scaleActorBoundingVolumes,
413  false,
414  customTest
415  ) == false) {
416  path.push_back(startPosition);
417  } else {
418  path.push_back(endPosition);
419  }
420  success = true;
421  }
422 
423  //
424  auto tries = 0;
425  auto endPositionIdx = 0;
426 
427  //
428  if (success == false) {
429  //
430  auto forcedAlternativeEndSteps = 0;
431 
432  // compute possible end positions
433  vector<Vector3> startPositionCandidates;
434  vector<Vector3> endPositionCandidates;
435  {
436  auto forwardVector = endPosition.clone().sub(startPosition).setY(0.0f).normalize();
437  auto sideVector = Vector3::computeCrossProduct(forwardVector, Vector3(0.0f, 1.0f, 0.0f)).setY(0.0f).normalize();
438  if (Float::isNaN(sideVector.getX()) ||
439  Float::isNaN(sideVector.getY()) ||
440  Float::isNaN(sideVector.getZ())) {
441  if (VERBOSE == true) Console::println("PathFinding::findPath(): side vector = NaN!");
442  endPositionCandidates.push_back(endPosition);
443  startPositionCandidates.push_back(startPosition);
444  } else {
445  {
446  auto sideDistance = stepSize;
447  auto forwardDistance = 0.0f;
448  auto i = 0;
449  while (true == true) {
450  endPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
451  i++; if (i >= alternativeEndSteps) break;
452  endPositionCandidates.push_back(Vector3().set(sideVector).scale(-sideDistance).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
453  i++; if (i >= alternativeEndSteps) break;
454  endPositionCandidates.push_back(Vector3().set(sideVector).scale(+sideDistance).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
455  i++; if (i >= alternativeEndSteps) break;
456  forwardDistance+= stepSize;
457  }
458  forwardDistance = 0.0f;
459  forwardVector.scale(-1.0f);
460  sideVector.scale(-1.0f);
461  startPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
462  startPositionCandidates.push_back(Vector3().set(sideVector).scale(-sideDistance).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
463  startPositionCandidates.push_back(Vector3().set(sideVector).scale(+sideDistance).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
464  startPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
465  startPositionCandidates.push_back(Vector3().set(sideVector).scale(-sideDistance).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
466  startPositionCandidates.push_back(Vector3().set(sideVector).scale(+sideDistance).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
467  forwardDistance+= stepSize;
468  startPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
469  startPositionCandidates.push_back(Vector3().set(sideVector).scale(-sideDistance).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
470  startPositionCandidates.push_back(Vector3().set(sideVector).scale(+sideDistance).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
471  startPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
472  startPositionCandidates.push_back(Vector3().set(sideVector).scale(-sideDistance).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
473  startPositionCandidates.push_back(Vector3().set(sideVector).scale(+sideDistance).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
474  forwardDistance+= stepSize;
475  startPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
476  startPositionCandidates.push_back(Vector3().set(sideVector).scale(-sideDistance).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
477  startPositionCandidates.push_back(Vector3().set(sideVector).scale(+sideDistance).add(forwardVector.clone().scale(-forwardDistance)).add(startPosition));
478  startPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
479  startPositionCandidates.push_back(Vector3().set(sideVector).scale(-sideDistance).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
480  startPositionCandidates.push_back(Vector3().set(sideVector).scale(+sideDistance).add(forwardVector.clone().scale(forwardDistance)).add(startPosition));
481  }
482  if (currentMaxTries == 0) {
483  forcedAlternativeEndSteps = 27 * 2;
484  auto forwardDistance = 0.0f;
485  auto i = 0;
486  while (true == true) {
487  endPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(forwardDistance)).add(endPosition));
488  i++; if (i >= forcedAlternativeEndSteps) break;
489  endPositionCandidates.push_back(Vector3().set(sideVector).scale(-0.3f).add(forwardVector.clone().scale(forwardDistance)).add(endPosition));
490  i++; if (i >= forcedAlternativeEndSteps) break;
491  endPositionCandidates.push_back(Vector3().set(sideVector).scale(-0.2f).add(forwardVector.clone().scale(forwardDistance)).add(endPosition));
492  i++; if (i >= forcedAlternativeEndSteps) break;
493  endPositionCandidates.push_back(Vector3().set(sideVector).scale(-0.1f).add(forwardVector.clone().scale(forwardDistance)).add(endPosition));
494  i++; if (i >= forcedAlternativeEndSteps) break;
495  endPositionCandidates.push_back(Vector3().set(sideVector).scale(+0.1f).add(forwardVector.clone().scale(forwardDistance)).add(endPosition));
496  i++; if (i >= forcedAlternativeEndSteps) break;
497  endPositionCandidates.push_back(Vector3().set(sideVector).scale(+0.2f).add(forwardVector.clone().scale(forwardDistance)).add(endPosition));
498  i++; if (i >= forcedAlternativeEndSteps) break;
499  endPositionCandidates.push_back(Vector3().set(sideVector).scale(+0.3f).add(forwardVector.clone().scale(forwardDistance)).add(endPosition));
500  i++; if (i >= forcedAlternativeEndSteps) break;
501  endPositionCandidates.push_back(Vector3().set(sideVector).scale(0.0f).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
502  i++; if (i >= forcedAlternativeEndSteps) break;
503  endPositionCandidates.push_back(Vector3().set(sideVector).scale(-0.3f).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
504  i++; if (i >= forcedAlternativeEndSteps) break;
505  endPositionCandidates.push_back(Vector3().set(sideVector).scale(-0.2f).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
506  i++; if (i >= forcedAlternativeEndSteps) break;
507  endPositionCandidates.push_back(Vector3().set(sideVector).scale(-0.1f).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
508  i++; if (i >= forcedAlternativeEndSteps) break;
509  endPositionCandidates.push_back(Vector3().set(sideVector).scale(+0.1f).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
510  i++; if (i >= forcedAlternativeEndSteps) break;
511  endPositionCandidates.push_back(Vector3().set(sideVector).scale(+0.2f).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
512  i++; if (i >= forcedAlternativeEndSteps) break;
513  endPositionCandidates.push_back(Vector3().set(sideVector).scale(+0.3f).add(forwardVector.clone().scale(-forwardDistance)).add(endPosition));
514  i++; if (i >= forcedAlternativeEndSteps) break;
515  forwardDistance+= 0.1f;
516  }
517  }
518  }
519  }
520 
521  Vector3 startPositionComputed = startPosition;
522  for (const auto& startPositionCandidate: startPositionCandidates) {
523  float startYHeight = startPositionCandidate.getY();
524  if (isWalkableInternal(
525  startPositionCandidate.getX(),
526  startPositionCandidate.getY(),
527  startPositionCandidate.getZ(),
528  startYHeight,
529  stepSize,
530  scaleActorBoundingVolumes,
531  false,
532  customTest
533  ) == false) {
534  if (VERBOSE == true) {
536  "PathFinding::findPath(): Start position not walkable: " +
537  to_string(startPositionCandidate.getX()) + ", " +
538  to_string(startPositionCandidate.getY()) + ", " +
539  to_string(startPositionCandidate.getZ()) + " / " +
540  to_string(startYHeight)
541  );
542  }
543  //
544  continue;
545  } else {
546  startPositionComputed = startPositionCandidate;
547  startPositionComputed.setY(startYHeight);
548  break;
549  }
550  }
551 
552  for (const auto& endPositionCandidate: endPositionCandidates) {
553  Vector3 endPositionComputed = endPositionCandidate;
554  float endYHeight = endPositionComputed.getY();
555  if (alternativeEndSteps > 0 &&
557  endPositionComputed.getX(),
558  endPositionComputed.getY(),
559  endPositionComputed.getZ(),
560  endYHeight,
561  stepSize,
562  scaleActorBoundingVolumes,
563  false,
564  customTest
565  ) == false) {
566  if (VERBOSE == true) {
568  "PathFinding::findPath(): End position not walkable: " +
569  to_string(endPositionComputed.getX()) + ", " +
570  to_string(endPositionComputed.getY()) + ", " +
571  to_string(endPositionComputed.getZ()) + " / " +
572  to_string(endYHeight)
573  );
574  }
575  //
576  continue;
577  } else {
578  endPositionComputed.setY(endYHeight);
579  }
580 
581  if (VERBOSE == true) {
583  "Finding path: " +
584  to_string(startPositionComputed.getX()) + ", " +
585  to_string(startPositionComputed.getY()) + ", " +
586  to_string(startPositionComputed.getZ()) + " --> " +
587  to_string(endPositionComputed.getX()) + ", " +
588  to_string(endPositionComputed.getY()) + ", " +
589  to_string(endPositionComputed.getZ())
590  );
591  }
592 
593  // end node + start node
594  {
595  // start node
596  auto start = pathFindingNodesPool.allocate();
597  start->hasPreviousNode = false;
598  start->position = startPositionComputed;
599  start->costsAll = 0.0f;
600  start->costsReachPoint = 0.0f;
601  start->costsEstimated = 0.0f;
602  start->x = getIntegerPositionComponent(start->position.getX(), stepSize);
603  start->y = 0;
604  start->z = getIntegerPositionComponent(start->position.getZ(), stepSize);
605  start->id = toId(
606  start->position.getX(),
607  start->position.getY(),
608  start->position.getZ(),
609  stepSize
610  );
611 
612  // end node
613  end.hasPreviousNode = false;
614  end.position = endPositionComputed;
615  end.costsAll = 0.0f;
616  end.costsReachPoint = 0.0f;
617  end.costsEstimated = 0.0f;
619  end.y = 0;
621  end.id = toId(
622  end.position.getX(),
623  end.position.getY(),
624  end.position.getZ(),
625  stepSize
626  );
627 
628  // set up start node costs
629  start->costsEstimated = computeDistanceToEnd(start);
630  start->costsAll = start->costsEstimated;
631 
632  // put to open nodes
633  openNodes[start->id] = start;
634  }
635 
636  // do the steps
637  bool done = false;
638  for (auto stepIdx = 0; done == false && stepIdx < stepsMax; stepIdx++) {
639  // check if there are still open nodes available
640  if (openNodes.size() == 0) {
641  done = true;
642  break;
643  }
644 
645  // choose node from open nodes thats least expensive to check its successors
646  PathFindingNode* endNode = nullptr;
647  PathFindingNode* endNodeCandidate = nullptr;
648  for (const auto& [openNodeId, openNode]: openNodes) {
649  if (equalsLastNode(openNode, &end) == true && (endNode == nullptr || openNode->costsAll < endNode->costsAll)) endNode = openNode;
650  if (endNodeCandidate == nullptr || openNode->costsAll < endNodeCandidate->costsAll) endNodeCandidate = openNode;
651  }
652 
653  //
654  if (endNodeCandidate == nullptr) {
655  done = true;
656  break;
657  }
658 
659  //
660  if (endNode != nullptr) {
661  end.hasPreviousNode = true;
662  end.previousNodeId = endNode->previousNodeId;
663  // Console::println("PathFinding::findPath(): path found with steps: " + to_string(stepIdx));
664  int nodesCount = 0;
665  unordered_map<tuple<int, int, int>, PathFindingNode*, PathFindingNodeId_Hash>::iterator nodeIt;
666  for (auto pathNode = &end; pathNode != nullptr; pathNode = (nodeIt = (pathNode->hasPreviousNode == false?closedNodes.end():closedNodes.find(pathNode->previousNodeId))) != closedNodes.end()?nodeIt->second:nullptr) {
667  nodesCount++;
668  // if (nodesCount > 0 && nodesCount % 100 == 0) {
669  // Console::println("PathFinding::findPath(): compute path: steps: " + to_string(nodesCount) + " / " + to_string(path.size()) + ": " + to_string((uint64_t)node));
670  // }
671  if (Float::isNaN(pathNode->position.getX()) ||
672  Float::isNaN(pathNode->position.getY()) ||
673  Float::isNaN(pathNode->position.getZ())) {
674  Console::println("PathFinding::findPath(): compute path: step: NaN");
675  done = true;
676  break;
677  }
678  path.push_back(pathNode->position);
679  }
680  reverse(path.begin(), path.end());
681  if (path.size() > 1) path.erase(path.begin());
682  if (path.size() == 0) {
683  // Console::println("PathFinding::findPath(): path with 0 steps: Fixing!");
684  path.push_back(endPositionComputed);
685  }
686  done = true;
687  success = true;
688  break;
689  } else {
690  const auto node = endNodeCandidate;
691  // do a step
692  step(node, stepSize, scaleActorBoundingVolumes, nullptr, false, customTest);
693  }
694  }
695 
696  // reset
697  openNodes.clear();
698  closedNodes.clear();
700 
701  //
702  tries++;
703 
704  //
705  if (success == true || tries >= currentMaxTries + forcedAlternativeEndSteps) break;
706  }
707 
708  //
709  if (tries == 0) {
710  Console::println("PathFinding::findPath(): end position were not walkable!");
711  }
712  }
713 
714  //
715  /*
716  if (stepIdx == stepsMax) {
717  Console::println("PathFinding::findPath(): steps == stepsMax: " + to_string(stepIdx));
718  }
719  */
720 
721  // unset actor bounding volume and remove rigid body
722  world->removeBody("tdme.pathfinding.actor");
723  world->removeBody("tdme.pathfinding.actor.slopetest");
724 
725  //
726  if (VERBOSE == true && tries > 1) Console::println("PathFinding::findPath(): time: " + to_string(Time::getCurrentMillis() - now) + "ms / " + to_string(tries) + " tries, success = " + to_string(success) + ", path steps: " + to_string(path.size()));
727 
728  // dispose custom test
729  if (customTest != nullptr) customTest->dispose();
730 
731  // return success
732  return success;
733 }
734 
735 FlowMap* PathFinding::createFlowMap(const vector<Vector3>& endPositions, const Vector3& center, float depth, float width, const uint16_t collisionTypeIds, const vector<Vector3>& path, bool complete, PathFindingCustomTest* customTest) {
736  // set up end position in costs map
737  if (path.empty() == true) {
738  Console::println("PathFinding::createFlowMap(): no path given");
739  return nullptr;
740  }
741 
742  // initialize custom test
743  if (customTest != nullptr) customTest->initialize();
744 
745  //
746  this->collisionTypeIds = collisionTypeIds;
747 
748  // init bounding volume, transform, collision body
749  auto actorBoundingVolume = make_unique<OrientedBoundingBox>(
750  Vector3(0.0f, actorHeight / 2.0f, 0.0f),
751  OrientedBoundingBox::AABB_AXIS_X,
752  OrientedBoundingBox::AABB_AXIS_Y,
753  OrientedBoundingBox::AABB_AXIS_Z,
755  );
756  // set up transform
757  Transform actorTransform;
758  actorTransform.setTranslation(endPositions[0]);
759  actorTransform.update();
760  world->addDynamicCollisionBody("tdme.pathfinding.actor", Body::COLLISION_TYPEID_RESERVED, true, actorTransform, { actorBoundingVolume.get() });
761 
762  // init bounding volume for slope testcollision body
763  auto actorBoundingVolumeSlopeTest = make_unique<OrientedBoundingBox>(
764  Vector3(0.0f, actorHeight / 2.0f, 0.0f),
765  OrientedBoundingBox::AABB_AXIS_X,
766  OrientedBoundingBox::AABB_AXIS_Y,
767  OrientedBoundingBox::AABB_AXIS_Z,
769  );
770  world->addDynamicCollisionBody("tdme.pathfinding.actor.slopetest", Body::COLLISION_TYPEID_RESERVED, true, actorTransform, { actorBoundingVolumeSlopeTest.get() });
771 
772  //
773  auto zMin = static_cast<int>(Math::ceil(-depth / 2.0f / flowMapStepSize));
774  auto zMax = static_cast<int>(Math::ceil(depth / 2.0f / flowMapStepSize));
775  auto xMin = static_cast<int>(Math::ceil(-width / 2.0f / flowMapStepSize));
776  auto xMax = static_cast<int>(Math::ceil(width / 2.0f / flowMapStepSize));
777  const vector<Vector3> emptyPath = { center };
778  const vector<Vector3>& pathToUse = path.empty() == false?path:emptyPath;
779 
780  // set up end position in costs map
781  if (endPositions.size() == 0) {
782  Console::println("PathFinding::createFlowMap(): no end positions given");
783  }
784 
785  // end node
786  end.hasPreviousNode = false;
787  end.position = path[0];
788  end.costsAll = 0.0f;
789  end.costsReachPoint = 0.0f;
790  end.costsEstimated = 0.0f;
792  end.y = 0;
794  end.id = toId(
795  end.position.getX(),
796  end.position.getY(),
797  end.position.getZ()
798  );
799 
800  // add end position to open nodes/start nodes
801  for (const auto& endPosition: endPositions) {
802  auto nodePosition = Vector3(
804  endPosition.getY(),
806  );
807  float nodeYHeight;
808  isWalkableInternal(nodePosition.getX(), nodePosition.getY(), nodePosition.getZ(), nodeYHeight, flowMapStepSize, flowMapScaleActorBoundingVolumes, true, customTest);
809  auto node = pathFindingNodesPool.allocate();
810  node->hasPreviousNode = false;
811  node->position.set(nodePosition).setY(nodeYHeight);
812  node->costsReachPoint = 0.0f;
813  node->x = FlowMap::getIntegerPositionComponent(nodePosition.getX(), flowMapStepSize);
814  node->y = 0;
815  node->z = FlowMap::getIntegerPositionComponent(nodePosition.getZ(), flowMapStepSize);
816  node->id = toIdInt(node->x, node->y, node->z);
817  // set up start node costs
818  node->costsEstimated = computeDistanceToEnd(node);
819  node->costsAll = node->costsEstimated;
820  // put to open nodes
821  openNodes[node->id] = node;
822  }
823 
824  // nodes to test
825  unordered_set<tuple<int, int, int>, PathFindingNodeId_Hash> nodesToTest;
826  for (const auto& _centerPathNode: pathToUse) {
827  auto centerPathNode = Vector3(
828  FlowMap::alignPositionComponent(_centerPathNode.getX(), flowMapStepSize),
829  _centerPathNode.getY(),
830  FlowMap::alignPositionComponent(_centerPathNode.getZ(), flowMapStepSize)
831  );
832  auto centerPathNodeX = FlowMap::getIntegerPositionComponent(centerPathNode.getX(), flowMapStepSize);
833  auto centerPathNodeZ = FlowMap::getIntegerPositionComponent(centerPathNode.getZ(), flowMapStepSize);
834  for (auto z = zMin; z <= zMax; z++) {
835  for (auto x = xMin; x <= xMax; x++) {
836  auto nodeId = toIdInt(
837  centerPathNodeX + x,
838  0,
839  centerPathNodeZ + z
840  );
841  nodesToTest.insert(nodeId);
842  }
843  }
844  }
845 
846  // do A* on open nodes
847  while (openNodes.size() > 0) {
848  // choose node from open nodes thats least expensive to check its successors
849  PathFindingNode* openNode = nullptr;
850  for (const auto& [openNodeCandidateId, openNodeCandidate]: openNodes) {
851  if (openNode == nullptr || openNodeCandidate->costsAll < openNode->costsAll) openNode = openNodeCandidate;
852  }
853  if (openNode == nullptr) break;
854 
855  // do a step
856  step(openNode, flowMapStepSize, flowMapScaleActorBoundingVolumes, &nodesToTest, true);
857  }
858 
859  // clear nodes to test
860  nodesToTest.clear();
861 
862  // generate flow map, which is based on
863  // see: https://howtorts.github.io/2014/01/04/basic-flow-fields.html
864  auto flowMap = new FlowMap(pathToUse, endPositions, flowMapStepSize, complete);
865  flowMap->acquireReference();
866  for (const auto& _centerPathNode: pathToUse) {
867  auto centerPathNode = Vector3(
868  FlowMap::alignPositionComponent(_centerPathNode.getX(), flowMapStepSize),
869  _centerPathNode.getY(),
870  FlowMap::alignPositionComponent(_centerPathNode.getZ(), flowMapStepSize)
871  );
872  auto centerPathNodeX = FlowMap::getIntegerPositionComponent(centerPathNode.getX(), flowMapStepSize);
873  auto centerPathNodeZ = FlowMap::getIntegerPositionComponent(centerPathNode.getZ(), flowMapStepSize);
874  for (auto z = zMin; z <= zMax; z++) {
875  for (auto x = xMin; x <= xMax; x++) {
876  auto cellPosition = Vector3(
877  static_cast<float>(centerPathNodeX) * flowMapStepSize + static_cast<float>(x) * flowMapStepSize,
878  0.0f,
879  static_cast<float>(centerPathNodeZ) * flowMapStepSize + static_cast<float>(z) * flowMapStepSize
880  );
881  auto cellId = FlowMap::toIdInt(
882  centerPathNodeX + x,
883  centerPathNodeZ + z
884  );
885  auto nodeId = toIdInt(
886  centerPathNodeX + x,
887  0,
888  centerPathNodeZ + z
889  );
890 
891  // do we already have this cell?
892  if (flowMap->hasCell(cellId) == true) continue;
893 
894  // walkable?
895  auto nodeIt = closedNodes.find(nodeId);
896  if (nodeIt == closedNodes.end()) {
897  continue;
898  }
899  auto node = nodeIt->second;
900  // set y
901  cellPosition.setY(node->position.getY());
902 
903  // check neighbours around our current cell
904  PathFindingNode* minCostsNode = nullptr;
905  auto minCosts = Float::MAX_VALUE;
906  for (auto _z = -1; _z <= 1; _z++)
907  for (auto _x = -1; _x <= 1; _x++)
908  if (_z != 0 || _x != 0) {
909  //
910  auto neighbourNodeId = toIdInt(
911  centerPathNodeX + x + _x,
912  0,
913  centerPathNodeZ + z + _z
914  );
915  // same node?
916  if (neighbourNodeId == nodeId) continue;
917  // do we have this cell?
918  auto neighbourNodeIt = closedNodes.find(neighbourNodeId);
919  if (neighbourNodeIt == closedNodes.end()) {
920  // nope
921  continue;
922  } else {
923  // yes && walkable
924  const auto& neighbourNode = neighbourNodeIt->second;
925  if (minCostsNode == nullptr || neighbourNode->costsReachPoint < minCosts) {
926  minCostsNode = neighbourNode;
927  minCosts = neighbourNode->costsReachPoint;
928  }
929  }
930  }
931  if (minCostsNode != nullptr) {
932  auto direction = minCostsNode->position.clone().sub(node->position).setY(0.0f).normalize();
933  if (Float::isNaN(direction.getX()) || Float::isNaN(direction.getY()) || Float::isNaN(direction.getZ())) {
935  to_string(minCostsNode->position.getX()) + ", " +
936  to_string(minCostsNode->position.getY()) + ", " +
937  to_string(minCostsNode->position.getZ()) + " -> " +
938  to_string(node->position.getX()) + ", " +
939  to_string(node->position.getY()) + ", " +
940  to_string(node->position.getZ()) + ": " +
941  to_string(minCostsNode == node) + "; " +
942  to_string(cellPosition.getX()) + ", " +
943  to_string(cellPosition.getY()) + ", " +
944  to_string(cellPosition.getZ())
945  );
946  }
947  flowMap->addCell(
948  cellId,
949  cellPosition,
950  true,
951  direction,
952  -1
953  );
954  }
955  }
956  }
957  }
958 
959  // reset
960  openNodes.clear();
961  closedNodes.clear();
963 
964  // do some post adjustments
965  for (const auto& _centerPathNode: pathToUse) {
966  auto centerPathNode = Vector3(
967  FlowMap::alignPositionComponent(_centerPathNode.getX(), flowMapStepSize),
968  _centerPathNode.getY(),
969  FlowMap::alignPositionComponent(_centerPathNode.getZ(), flowMapStepSize)
970  );
971  auto centerPathNodeX = FlowMap::getIntegerPositionComponent(centerPathNode.getX(), flowMapStepSize);
972  auto centerPathNodeZ = FlowMap::getIntegerPositionComponent(centerPathNode.getZ(), flowMapStepSize);
973  for (auto z = zMin; z <= zMax; z++) {
974  for (auto x = xMin; x <= xMax; x++) {
975  auto cellPosition = Vector3(
976  static_cast<float>(centerPathNodeX) * flowMapStepSize + static_cast<float>(x) * flowMapStepSize,
977  0.0f,
978  static_cast<float>(centerPathNodeZ) * flowMapStepSize + static_cast<float>(z) * flowMapStepSize
979  );
980  auto cellId = FlowMap::toIdInt(
981  centerPathNodeX + x,
982  centerPathNodeZ + z
983  );
984  auto cell = flowMap->getCell(cellId);
985  if (cell == nullptr) continue;
986  auto topCell = flowMap->getCell(FlowMap::toIdInt(centerPathNodeX + x, centerPathNodeZ + z - 1));
987  if (topCell == nullptr && Math::abs(cell->getDirection().getX()) > 0.0f && cell->getDirection().getZ() < 0.0f){
988  cell->setDirection(cell->getDirection().clone().setZ(0.0f).normalize());
989  }
990  auto bottomCell = flowMap->getCell(FlowMap::toIdInt(centerPathNodeX + x, centerPathNodeZ + z + 1));
991  if (bottomCell == nullptr && Math::abs(cell->getDirection().getX()) > 0.0f && cell->getDirection().getZ() > 0.0f){
992  cell->setDirection(cell->getDirection().clone().setZ(0.0f).normalize());
993  }
994  auto leftCell = flowMap->getCell(FlowMap::toIdInt(centerPathNodeX + x - 1, centerPathNodeZ + z));
995  if (leftCell == nullptr && cell->getDirection().getX() < 0.0f && Math::abs(cell->getDirection().getZ()) > 0.0f){
996  cell->setDirection(cell->getDirection().clone().setX(0.0f).normalize());
997  }
998  auto rightCell = flowMap->getCell(FlowMap::toIdInt(centerPathNodeX + x + 1, centerPathNodeZ + z));
999  if (rightCell == nullptr && cell->getDirection().getX() > 0.0f && Math::abs(cell->getDirection().getZ()) > 0.0f){
1000  cell->setDirection(cell->getDirection().clone().setX(0.0f).normalize());
1001  }
1002  }
1003  }
1004  }
1005 
1006  // do some more post adjustments
1007  Vector3 lastCenterPathNode = pathToUse.size() < 2?Vector3():pathToUse[0] - (pathToUse[1] - pathToUse[0]);
1008  unordered_set<string> cellsProcessed;
1009  for (int i = pathToUse.size() - 1; i >= 0; i--) {
1010  auto _centerPathNode = pathToUse[i];
1011  auto centerPathNode = Vector3(
1012  FlowMap::alignPositionComponent(_centerPathNode.getX(), flowMapStepSize),
1013  _centerPathNode.getY(),
1014  FlowMap::alignPositionComponent(_centerPathNode.getZ(), flowMapStepSize)
1015  );
1016  auto pathDirection = (lastCenterPathNode - centerPathNode).setY(0.0f).normalize();
1017  auto centerPathNodeX = FlowMap::getIntegerPositionComponent(centerPathNode.getX(), flowMapStepSize);
1018  auto centerPathNodeZ = FlowMap::getIntegerPositionComponent(centerPathNode.getZ(), flowMapStepSize);
1019  for (auto z = zMin; z <= zMax; z++) {
1020  for (auto x = xMin; x <= xMax; x++) {
1021  auto cellId = FlowMap::toIdInt(
1022  centerPathNodeX + x,
1023  centerPathNodeZ + z
1024  );
1025  auto cell = flowMap->getCell(cellId);
1026  if (cell == nullptr) continue;
1027 
1028  // check if we have missing neighbour cells
1029  auto hadMissingNeighborCell = false;
1030  for (auto nZ = -1; nZ < 2 && hadMissingNeighborCell == false; nZ++) {
1031  for (auto nX = -1; nX < 2 && hadMissingNeighborCell == false; nX++) {
1032  if (nZ == 0 && nX == 0) continue;
1033  auto neighbourCellId = FlowMap::toIdInt(
1034  centerPathNodeX + x + nX,
1035  centerPathNodeZ + z + nZ
1036  );
1037  auto neighbourCell = flowMap->getCell(neighbourCellId);
1038  if (neighbourCell == nullptr) {
1039  cell->setMissingNeighborCell(true);
1040  hadMissingNeighborCell = true;
1041  break;
1042  }
1043  }
1044  }
1045 
1046  // determine path node index
1047  {
1048  auto i = 0;
1049  auto pathNodeIdx = -1;
1050  auto pathNodeNodeDistanceSquared = Float::MAX_VALUE;
1051  for (const auto& pathNode: pathToUse) {
1052  auto pathNodeCellAxis = pathNode - cell->getPosition();
1053  auto pathNodeCandidateDistanceSquared = pathNodeCellAxis.computeLengthSquared();
1054  if (pathNodeIdx == -1 || pathNodeCandidateDistanceSquared < pathNodeNodeDistanceSquared) {
1055  pathNodeIdx = i;
1056  pathNodeNodeDistanceSquared = pathNodeCandidateDistanceSquared;
1057  }
1058  i++;
1059  }
1060  cell->setPathNodeIdx(pathNodeIdx < pathToUse.size() - 1?pathNodeIdx + 1:pathNodeIdx);
1061  }
1062  }
1063  }
1064  lastCenterPathNode = centerPathNode;
1065  }
1066 
1067  // unset actor bounding volume and remove rigid body
1068  world->removeBody("tdme.pathfinding.actor");
1069  world->removeBody("tdme.pathfinding.actor.slopetest");
1070 
1071  // dispose custom test
1072  if (customTest != nullptr) customTest->dispose();
1073 
1074  //
1075  return flowMap;
1076 }
1077 
1078 const vector<Vector3> PathFinding::generateDirectPath(const Vector3& start, const Vector3& end) {
1079  vector<Vector3> path;
1080  Vector3 axis;
1081  axis.set(end).sub(start);
1082  auto length = axis.computeLength();
1083  auto step = axis.clone().normalize() * stepSize;
1084  Vector3 current = start;
1085  for (auto i = stepSize; i < length; i+= stepSize) {
1086  path.push_back(current.add(step));
1087  }
1088  path.push_back(end);
1089  return path;
1090 }
Texture entity.
Definition: Texture.h:24
ByteBuffer * getTextureData()
Definition: Texture.h:272
uint16_t getTextureWidth() const
Definition: Texture.h:218
Transform which contain scale, rotations and translation.
Definition: Transform.h:29
void setTranslation(const Vector3 &translation)
Set translation.
Definition: Transform.h:64
virtual void update()
Computes transform matrix.
Definition: Transform.cpp:33
void addRotation(const Vector3 &axis, const float angle)
Add rotation.
Definition: Transform.h:113
Rigid body class.
Definition: Body.h:41
void setTransform(const Transform &transform)
Set transform.
Definition: Body.h:383
Dynamic physics world class.
Definition: World.h:38
bool doesCollideWith(uint16_t collisionTypeIds, Body *body, vector< Body * > &collisionBodies)
Check if world collides with given body.
Definition: World.cpp:489
void removeBody(const string &id)
Removes body identified by id.
Definition: World.cpp:211
Body * getBody(const string &id)
Returns body identified by id.
Definition: World.h:151
Body * determineHeight(uint16_t collisionTypeIds, float stepUpMax, const Vector3 &point, Vector3 &heightPoint, float minHeight=-10000.0f, float maxHeight=10000.0f)
Determine height on x,y,u while respecting step up max.
Definition: World.cpp:405
Body * addDynamicCollisionBody(const string &id, uint16_t collisionTypeId, bool enabled, const Transform &transform, const vector< BoundingVolume * > &boundingVolumes, bool hierarchy=false)
Add a dynamic collision body.
Definition: World.cpp:158
Oriented bounding box physics primitive.
Standard math functions.
Definition: Math.h:19
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
float getY() const
Definition: Vector3.h:117
float getX() const
Definition: Vector3.h:100
float computeLength() const
Definition: Vector3.h:274
float getZ() const
Definition: Vector3.h:134
Vector3 & add(float scalar)
Adds a scalar.
Definition: Vector3.h:153
Vector3 & setY(float y)
Sets y component.
Definition: Vector3.h:126
float computeLengthSquared() const
Definition: Vector3.h:281
Vector3 clone() const
Clones this vector3.
Definition: Vector3.h:374
Vector3 & sub(float scalar)
Subtracts a scalar.
Definition: Vector3.h:177
Vector3 & set(float x, float y, float z)
Sets this vector3 by its components.
Definition: Vector3.h:70
Vector3 & normalize()
Normalizes this vector3.
Definition: Vector3.h:239
uint8_t get(int64_t position) const
Definition: Buffer.h:107
Console class.
Definition: Console.h:29
static void println()
Print new line to console.
Definition: Console.cpp:92
Float class.
Definition: Float.h:27
static constexpr float MAX_VALUE
Definition: Float.h:29
static bool isNaN(float value)
Check if float is not a number.
Definition: Float.h:66
int getIntegerPositionComponent(float value) const
Returns integer position component.
Definition: FlowMap.h:130
static const tuple< int, int > toIdInt(int x, int z)
Return string representation of given x,z integer flow map position representation for flow map cell ...
Definition: FlowMap.h:150
static float alignPositionComponent(float value, float stepSize)
Align position component.
Definition: FlowMap.h:113
Path finding class.
Definition: PathFinding.h:51
unordered_map< tuple< uint8_t, uint8_t, int, int, int, uint16_t, bool >, float, WalkableCache_Hash > walkableCache
Definition: PathFinding.h:475
PathFindingNodesPool pathFindingNodesPool
Definition: PathFinding.h:474
unordered_map< tuple< int, int, int >, PathFindingNode *, PathFindingNodeId_Hash > closedNodes
Definition: PathFinding.h:473
const vector< Vector3 > generateDirectPath(const Vector3 &start, const Vector3 &end)
Generate direct path from start to end.
FlowMap * createFlowMap(const vector< Vector3 > &endPositions, const Vector3 &center, float depth, float width, const uint16_t collisionTypeIds, const vector< Vector3 > &path=vector< Vector3 >(), bool complete=true, PathFindingCustomTest *customTest=nullptr)
Create flow map.
bool isWalkable(float x, float y, float z, float &height, float stepSize, float scaleActorBoundingVolumes, bool flowMapRequest, uint16_t collisionTypeIds=0, bool ignoreStepUpMax=false)
Checks if a cell is walkable.
bool equalsLastNode(const PathFindingNode *a, const PathFindingNode *b)
Returns if nodes are equals for (last node test)
Definition: PathFinding.h:410
static const tuple< int, int, int > toIdInt(int x, int y, int z)
Return string representation of given x,z integer flow map position representation for path finding i...
Definition: PathFinding.h:184
bool isSlopeWalkableInternal(float x, float y, float z, float successorX, float successorY, float successorZ, float stepSize, float scaleActorBoundingVolumes, bool flowMapRequest, PathFindingCustomTest *customTest=nullptr, uint16_t collisionTypeIds=0)
Checks if a cell is slope walkable.
float computeDistance(const PathFindingNode *a, const PathFindingNode *b)
Computes non square rooted distance between a and b.
Definition: PathFinding.h:379
float computeDistanceToEnd(const PathFindingNode *node)
Computes minimal non square rooted distance between node and end point.
Definition: PathFinding.h:388
bool equals(const PathFindingNode *a, float bX, float bY, float bZ)
Returns if nodes are equals.
Definition: PathFinding.h:400
static constexpr bool VERBOSE
Definition: PathFinding.h:53
static int getIntegerPositionComponent(float value, float stepSize)
Returns integer position component.
Definition: PathFinding.h:164
bool findPathCustom(const Vector3 &startPosition, const Vector3 &endPosition, float stepSize, float scaleActorBoundingVolumes, const uint16_t collisionTypeIds, vector< Vector3 > &path, int alternativeEndSteps=0, int maxTriesOverride=-1, PathFindingCustomTest *customTest=nullptr)
Finds path to given end position.
const tuple< int, int, int > toId(float x, float y, float z)
Return path finding node id of given x, y, z position components.
Definition: PathFinding.h:121
bool isWalkableInternal(float x, float y, float z, float &height, float stepSize, float scaleActorBoundingVolumes, bool flowMapRequest, PathFindingCustomTest *customTest=nullptr, uint16_t collisionTypeIds=0, bool ignoreStepUpMax=false)
Checks if a cell is walkable.
Definition: PathFinding.cpp:87
unordered_map< tuple< int, int, int >, PathFindingNode *, PathFindingNodeId_Hash > openNodes
Definition: PathFinding.h:472
void step(PathFindingNode *node, float stepSize, float scaleActorBoundingVolumes, const unordered_set< tuple< int, int, int >, PathFindingNodeId_Hash > *nodesToTest, bool flowMapRequest, PathFindingCustomTest *customTest=nullptr)
Processes one step in AStar path finding.
Pool template class.
Definition: Pool.h:20
void release(T *element)
Release element in pool for being reused.
Definition: Pool.h:71
T * allocate()
Allocate a new element from pool.
Definition: Pool.h:55
void reset()
Reset this pool.
Definition: Pool.h:98
virtual void releaseReference()
Releases a reference, thus decrementing the counter and delete it if reference counter is zero.
Definition: Reference.h:38
Terrain utility.
Definition: Terrain.h:33
static constexpr float STEP_SIZE
Definition: Terrain.h:35
Time utility class.
Definition: Time.h:20
static int64_t getCurrentMillis()
Retrieve current time in milliseconds.
Definition: Time.h:27
Path finding custom test interface.
virtual void initialize()=0
Initialize custom test for a sequence of isWalkable calls for the current path finding execution.
virtual void dispose()=0
Disposes custom test after a sequence of isWalkable calls for the last path finding execution.
virtual bool isWalkable(PathFinding *pathFinding, float x, float y, float z)=0
Is walkable user test.
int y
Integer position along y axis.
Definition: PathFinding.h:323
int z
Integer position along z axis.
Definition: PathFinding.h:328
tuple< int, int, int > id
Node id.
Definition: PathFinding.h:283
int x
Integer position along x axis.
Definition: PathFinding.h:318
tuple< int, int, int > previousNodeId
Previous node.
Definition: PathFinding.h:293
float costsAll
Estimated costs to the end plus calculated costs from start to this node.
Definition: PathFinding.h:303
float costsEstimated
Estimated costs to get to the end.
Definition: PathFinding.h:313
float costsReachPoint
Calculated costs up to this point (from all previous nodes up to this node) to get to this coordinate...
Definition: PathFinding.h:308