Răsfoiți Sursa

Added functionality to NavigationMesh.

Lasse Öörni 12 ani în urmă
părinte
comite
362e6a6f95

+ 2 - 1
Bin/Data/Scripts/Navigation.as

@@ -333,6 +333,8 @@ void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
 
         if (result.drawable !is null)
         {
+            NavigationMesh@ navMesh = testScene.GetComponent("NavigationMesh");
+
             Vector3 rayHitPos = cameraRay.origin + cameraRay.direction * result.distance;
             if (setStartPos)
             {
@@ -347,7 +349,6 @@ void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
 
             if (startPosSet && endPosSet)
             {
-                NavigationMesh@ navMesh = testScene.GetComponent("NavigationMesh");
                 path = navMesh.FindPath(startPos, endPos);
             }
         }

+ 3 - 0
Docs/ScriptAPI.dox

@@ -5400,6 +5400,9 @@ Methods:<br>
 - bool Build()
 - bool Build(const BoundingBox&)
 - Vector3[]@ FindPath(const Vector3&, const Vector3&, const Vector3& arg2 = Vector3 ( 1.0 , 1.0 , 1.0 ))
+- Vector3 GetRandomPoint()
+- Vector3 GetRandomPointInCircle(const Vector3&, float, const Vector3& arg2 = Vector3 ( 1.0 , 1.0 , 1.0 ))
+- Vector3 Raycast(const Vector3&, const Vector3&, const Vector3& arg2 = Vector3 ( 1.0 , 1.0 , 1.0 ))
 
 Properties:<br>
 - ShortStringHash type (readonly)

+ 3 - 0
Engine/Engine/NavigationAPI.cpp

@@ -48,6 +48,9 @@ void RegisterNavigationMesh(asIScriptEngine* engine)
     engine->RegisterObjectMethod("NavigationMesh", "bool Build()", asMETHODPR(NavigationMesh, Build, (void), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "bool Build(const BoundingBox&in)", asMETHODPR(NavigationMesh, Build, (const BoundingBox&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "Array<Vector3>@ FindPath(const Vector3&in, const Vector3&in, const Vector3&in extents = Vector3(1.0, 1.0, 1.0))", asFUNCTION(NavigationMeshFindPath), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("NavigationMesh", "Vector3 GetRandomPoint()", asMETHOD(NavigationMesh, GetRandomPoint), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "Vector3 GetRandomPointInCircle(const Vector3&in, float, const Vector3&in extents = Vector3(1.0, 1.0, 1.0))", asMETHOD(NavigationMesh, GetRandomPointInCircle), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "Vector3 Raycast(const Vector3&in, const Vector3&in, const Vector3&in extents = Vector3(1.0, 1.0, 1.0))", asMETHOD(NavigationMesh, Raycast), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "void set_tileSize(int)", asMETHOD(NavigationMesh, SetTileSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "int get_tileSize() const", asMETHOD(NavigationMesh, GetTileSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "void set_cellSize(float)", asMETHOD(NavigationMesh, SetCellSize), asCALL_THISCALL);

+ 1 - 1
Engine/Navigation/Navigable.h

@@ -27,7 +27,7 @@
 namespace Urho3D
 {
 
-/// Navigable component, tags geometry for inclusion in the navigation mesh. Optionally auto-includes geometry also from child nodes.
+/// Component which tags geometry for inclusion in the navigation mesh. Optionally auto-includes geometry from child nodes.
 class Navigable : public Component
 {
     OBJECT(Navigable);

+ 89 - 8
Engine/Navigation/NavigationMesh.cpp

@@ -37,6 +37,7 @@
 #include "TerrainPatch.h"
 #include "VectorBuffer.h"
 
+#include <cfloat>
 #include <DetourNavMesh.h>
 #include <DetourNavMeshBuilder.h>
 #include <DetourNavMeshQuery.h>
@@ -427,10 +428,10 @@ void NavigationMesh::FindPath(PODVector<Vector3>& dest, const Vector3& start, co
     
     // Navigation data is in local space. Transform path points from world to local
     const Matrix3x4& transform = node_->GetWorldTransform();
-    Matrix3x4 inverseTransform = transform.Inverse();
+    Matrix3x4 inverse = transform.Inverse();
     
-    Vector3 localStart = inverseTransform * start;
-    Vector3 localEnd = inverseTransform * end;
+    Vector3 localStart = inverse * start;
+    Vector3 localEnd = inverse * end;
     
     dtPolyRef startRef;
     dtPolyRef endRef;
@@ -462,11 +463,91 @@ void NavigationMesh::FindPath(PODVector<Vector3>& dest, const Vector3& start, co
         dest.Push(transform * pathData_->pathPoints_[i]);
 }
 
+Vector3 NavigationMesh::GetRandomPoint()
+{
+    if (!navMesh_ || !node_)
+        return Vector3::ZERO;
+    
+    if (!navMeshQuery_)
+    {
+        if (!InitializeQuery())
+            return Vector3::ZERO;
+    }
+    
+    dtPolyRef polyRef;
+    Vector3 point(Vector3::ZERO);
+    
+    navMeshQuery_->findRandomPoint(queryFilter_, Random, &polyRef, &point.x_);
+    
+    return node_->GetWorldTransform() * point;
+}
+
+Vector3 NavigationMesh::GetRandomPointInCircle(const Vector3& center, float radius, const Vector3& extents)
+{
+    if (!navMesh_ || !node_)
+        return Vector3::ZERO;
+    
+    if (!navMeshQuery_)
+    {
+        if (!InitializeQuery())
+            return Vector3::ZERO;
+    }
+    
+    const Matrix3x4& transform = node_->GetWorldTransform();
+    Matrix3x4 inverse = transform.Inverse();
+    Vector3 localCenter = inverse * center;
+    
+    dtPolyRef startRef;
+    navMeshQuery_->findNearestPoly(&localCenter.x_, &extents.x_, queryFilter_, &startRef, 0);
+    if (!startRef)
+        return center;
+    
+    dtPolyRef polyRef;
+    Vector3 point(localCenter);
+    
+    navMeshQuery_->findRandomPointAroundCircle(startRef, &localCenter.x_, radius, queryFilter_, Random, &polyRef, &point.x_);
+    
+    return transform * point;
+}
+
 BoundingBox NavigationMesh::GetWorldBoundingBox() const
 {
     return node_ ? boundingBox_.Transformed(node_->GetWorldTransform()) : boundingBox_;
 }
 
+Vector3 NavigationMesh::Raycast(const Vector3& start, const Vector3& end, const Vector3& extents)
+{
+    if (!navMesh_ || !node_)
+        return Vector3::ZERO;
+    
+    if (!navMeshQuery_)
+    {
+        if (!InitializeQuery())
+            return Vector3::ZERO;
+    }
+    
+    const Matrix3x4& transform = node_->GetWorldTransform();
+    Matrix3x4 inverse = transform.Inverse();
+    
+    Vector3 localStart = inverse * start;
+    Vector3 localEnd = inverse * end;
+    
+    dtPolyRef startRef;
+    navMeshQuery_->findNearestPoly(&localStart.x_, &extents.x_, queryFilter_, &startRef, 0);
+    if (!startRef)
+        return start;
+    
+    Vector3 localHitNormal;
+    float t;
+    int numPolys;
+    
+    navMeshQuery_->raycast(startRef, &localStart.x_, &localEnd.x_, queryFilter_, &t, &localHitNormal.x_, pathData_->polys_, &numPolys, MAX_POLYS);
+    if (t == FLT_MAX)
+        t = 1.0f;
+    
+    return start.Lerp(end, t);
+}
+
 void NavigationMesh::SetNavigationDataAttr(PODVector<unsigned char> data)
 {
     ReleaseNavigationMesh();
@@ -593,7 +674,7 @@ void NavigationMesh::CollectGeometries(Vector<NavigationGeometryInfo>& geometryL
         return;
     processedNodes.Insert(node);
     
-    Matrix3x4 inverseTransform = node_->GetWorldTransform().Inverse();
+    Matrix3x4 inverse = node_->GetWorldTransform().Inverse();
     
     // Prefer compatible physics collision shapes (triangle mesh, convex hull, box) if found.
     // Then fallback to visible geometry
@@ -616,8 +697,8 @@ void NavigationMesh::CollectGeometries(Vector<NavigationGeometryInfo>& geometryL
             scaleMatrix.SetScale(shape->GetSize());
             
             info.component_ = shape;
-            info.transform_ = inverseTransform * node->GetWorldTransform() * scaleMatrix;
-            info.boundingBox_ = shape->GetWorldBoundingBox().Transformed(inverseTransform);
+            info.transform_ = inverse * node->GetWorldTransform() * scaleMatrix;
+            info.boundingBox_ = shape->GetWorldBoundingBox().Transformed(inverse);
             
             geometryList.Push(info);
             collisionShapeFound = true;
@@ -646,8 +727,8 @@ void NavigationMesh::CollectGeometries(Vector<NavigationGeometryInfo>& geometryL
                 continue;
             
             info.component_ = drawable;
-            info.transform_ = inverseTransform * node->GetWorldTransform();
-            info.boundingBox_ = drawable->GetWorldBoundingBox().Transformed(inverseTransform);
+            info.transform_ = inverse * node->GetWorldTransform();
+            info.boundingBox_ = drawable->GetWorldBoundingBox().Transformed(inverse);
             
             geometryList.Push(info);
         }

+ 6 - 1
Engine/Navigation/NavigationMesh.h

@@ -100,7 +100,12 @@ public:
     bool Build(const BoundingBox& boundingBox);
     /// Find a path between world space points. Return non-empty list of points if successful.
     void FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, const Vector3& extents = Vector3::ONE);
-    
+    /// Return a random point on the navigation mesh.
+    Vector3 GetRandomPoint();
+    /// Return a random point on the navigation mesh within a circle. The circle radius is only a guideline and in practice the returned point may be further away.
+    Vector3 GetRandomPointInCircle(const Vector3& center, float radius, const Vector3& extents = Vector3::ONE);
+    /// Perform a walkability raycast on the navigation mesh between start and end and return the point where a wall was hit, or the end point if no walls.
+    Vector3 Raycast(const Vector3& start, const Vector3& end, const Vector3& extents = Vector3::ONE);
     /// Return tile size.
     int GetTileSize() const { return tileSize_; }
     /// Return cell size.