Browse Source

Merge pull request #107 from AtomicGameEngine/JME-WORK-UPDATES

Updates for navigation meshes, bullet physics, texture getting for render targets, fix incorrect viewport size for fullscreen filters
JoshEngebretson 10 years ago
parent
commit
bba0172f1b
30 changed files with 591 additions and 235 deletions
  1. 10 3
      Source/Atomic/Atomic2D/AnimationSet2D.cpp
  2. 4 3
      Source/Atomic/Graphics/Direct3D11/D3D11Graphics.cpp
  3. 8 0
      Source/Atomic/Graphics/Direct3D11/D3D11Texture.cpp
  4. 2 0
      Source/Atomic/Graphics/Direct3D11/D3D11Texture.h
  5. 4 3
      Source/Atomic/Graphics/Direct3D9/D3D9Graphics.cpp
  6. 9 1
      Source/Atomic/Graphics/Direct3D9/D3D9Texture.cpp
  7. 2 0
      Source/Atomic/Graphics/Direct3D9/D3D9Texture.h
  8. 41 6
      Source/Atomic/Graphics/Direct3D9/D3D9Texture2D.cpp
  9. 40 5
      Source/Atomic/Graphics/Direct3D9/D3D9TextureCube.cpp
  10. 6 5
      Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp
  11. 8 0
      Source/Atomic/Graphics/OpenGL/OGLTexture.cpp
  12. 2 0
      Source/Atomic/Graphics/OpenGL/OGLTexture.h
  13. 8 0
      Source/Atomic/Graphics/RenderPath.cpp
  14. 7 4
      Source/Atomic/Graphics/RenderPath.h
  15. 9 6
      Source/Atomic/Graphics/View.cpp
  16. 113 123
      Source/Atomic/Navigation/CrowdAgent.cpp
  17. 19 12
      Source/Atomic/Navigation/CrowdAgent.h
  18. 66 16
      Source/Atomic/Navigation/DetourCrowdManager.cpp
  19. 14 6
      Source/Atomic/Navigation/DetourCrowdManager.h
  20. 84 16
      Source/Atomic/Navigation/DynamicNavigationMesh.cpp
  21. 14 0
      Source/Atomic/Navigation/DynamicNavigationMesh.h
  22. 1 0
      Source/Atomic/Navigation/NavigationEvents.h
  23. 41 4
      Source/Atomic/Navigation/NavigationMesh.cpp
  24. 18 3
      Source/Atomic/Navigation/NavigationMesh.h
  25. 5 0
      Source/Atomic/Navigation/Obstacle.cpp
  26. 9 7
      Source/Atomic/Navigation/OffMeshConnection.cpp
  27. 6 6
      Source/Atomic/Navigation/OffMeshConnection.h
  28. 18 2
      Source/Atomic/Physics/CollisionShape.cpp
  29. 3 0
      Source/ThirdParty/DetourTileCache/include/DetourTileCache.h
  30. 20 4
      Source/Tools/AssetImporter/AssetImporter.cpp

+ 10 - 3
Source/Atomic/Atomic2D/AnimationSet2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -331,6 +331,8 @@ bool AnimationSet2D::LoadSpriterAnimation(const XMLElement& animationElem)
     if (animationElem.HasAttribute("looping"))
     if (animationElem.HasAttribute("looping"))
         looped = animationElem.GetBool("looping");
         looped = animationElem.GetBool("looping");
 
 
+    float highestKeyTime = 0.0f;
+
     // Load timelines
     // Load timelines
     Vector<SpriterTimeline2D> timelines;
     Vector<SpriterTimeline2D> timelines;
     for (XMLElement timelineElem = animationElem.GetChild("timeline"); timelineElem; timelineElem = timelineElem.GetNext("timeline"))
     for (XMLElement timelineElem = animationElem.GetChild("timeline"); timelineElem; timelineElem = timelineElem.GetNext("timeline"))
@@ -346,6 +348,7 @@ bool AnimationSet2D::LoadSpriterAnimation(const XMLElement& animationElem)
         {
         {
             SpriterTimelineKey2D key;
             SpriterTimelineKey2D key;
             key.time_ = keyElem.GetFloat("time") * 0.001f;
             key.time_ = keyElem.GetFloat("time") * 0.001f;
+            highestKeyTime = Max(highestKeyTime, key.time_);
             if (keyElem.HasAttribute("spin"))
             if (keyElem.HasAttribute("spin"))
                 key.spin_ = keyElem.GetInt("spin");
                 key.spin_ = keyElem.GetInt("spin");
 
 
@@ -436,6 +439,10 @@ bool AnimationSet2D::LoadSpriterAnimation(const XMLElement& animationElem)
 
 
     // Create animation
     // Create animation
     SharedPtr<Animation2D> animation(new Animation2D(this));
     SharedPtr<Animation2D> animation(new Animation2D(this));
+    // Crop animation length if longer than the last keyframe, prevents sprites vanishing in clamp mode, or occasional flashes
+    // when looped
+    if (length > highestKeyTime)
+        length = highestKeyTime;
     animation->SetName(name);
     animation->SetName(name);
     animation->SetLength(length);
     animation->SetLength(length);
     animation->SetLooped(looped);
     animation->SetLooped(looped);
@@ -462,7 +469,7 @@ bool AnimationSet2D::LoadSpriterAnimation(const XMLElement& animationElem)
 
 
             keyFrame.time_ = timelineKey.time_;
             keyFrame.time_ = timelineKey.time_;
 
 
-            // Set diabled
+            // Set disabled
             keyFrame.enabled_ = false;
             keyFrame.enabled_ = false;
             keyFrame.parent_ = timeline.parent_;
             keyFrame.parent_ = timeline.parent_;
             keyFrame.transform_ = Transform2D(timelineKey.position_, timelineKey.angle_, timelineKey.scale_);
             keyFrame.transform_ = Transform2D(timelineKey.position_, timelineKey.angle_, timelineKey.scale_);
@@ -472,7 +479,7 @@ bool AnimationSet2D::LoadSpriterAnimation(const XMLElement& animationElem)
             {
             {
                 keyFrame.sprite_ = timelineKey.sprite_;
                 keyFrame.sprite_ = timelineKey.sprite_;
                 keyFrame.alpha_ = timelineKey.alpha_;
                 keyFrame.alpha_ = timelineKey.alpha_;
-                keyFrame.useHotSpot_ = timelineKey.useHotSpot_;                
+                keyFrame.useHotSpot_ = timelineKey.useHotSpot_;
                 if (timelineKey.useHotSpot_)
                 if (timelineKey.useHotSpot_)
                     keyFrame.hotSpot_ = timelineKey.hotSpot_;
                     keyFrame.hotSpot_ = timelineKey.hotSpot_;
             }
             }

+ 4 - 3
Source/Atomic/Graphics/Direct3D11/D3D11Graphics.cpp

@@ -58,9 +58,10 @@
 #pragma warning(disable:4355)
 #pragma warning(disable:4355)
 #endif
 #endif
 
 
-// On Intel / NVIDIA setups prefer the NVIDIA GPU
+// Prefer the high-performance GPU on switchable GPU systems
 extern "C" {
 extern "C" {
-    __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+    __declspec(dllexport) DWORD NvOptimusEnablement = 1;
+    __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
 }
 }
 
 
 namespace Atomic
 namespace Atomic
@@ -2194,7 +2195,7 @@ bool Graphics::OpenWindow(int width, int height, bool resizable, bool borderless
 
 
     if (!impl_->window_)
     if (!impl_->window_)
     {
     {
-        LOGERROR("Could not create window");
+        LOGERRORF("Could not create window, root cause: '%s'", SDL_GetError());
         return false;
         return false;
     }
     }
 
 

+ 8 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Texture.cpp

@@ -248,6 +248,14 @@ unsigned Texture::GetRowDataSize(int width) const
     }
     }
 }
 }
 
 
+unsigned Texture::GetComponents() const
+{
+    if (!width_ || IsCompressed())
+        return 0;
+    else
+        return GetRowDataSize(width_) / width_;
+}
+
 void Texture::SetParameters(XMLFile* file)
 void Texture::SetParameters(XMLFile* file)
 {
 {
     if (!file)
     if (!file)

+ 2 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Texture.h

@@ -102,6 +102,8 @@ public:
     unsigned GetDataSize(int width, int height, int depth) const;
     unsigned GetDataSize(int width, int height, int depth) const;
     /// Return data size in bytes for a pixel or block row.
     /// Return data size in bytes for a pixel or block row.
     unsigned GetRowDataSize(int width) const;
     unsigned GetRowDataSize(int width) const;
+    /// Return number of image components required to receive pixel data from GetData(), or 0 for compressed images.
+    unsigned GetComponents() const;
     /// Return whether the parameters are dirty.
     /// Return whether the parameters are dirty.
     bool GetParametersDirty() const { return parametersDirty_ || !sampler_; }
     bool GetParametersDirty() const { return parametersDirty_ || !sampler_; }
 
 

+ 4 - 3
Source/Atomic/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -56,9 +56,10 @@
 #pragma warning(disable:4355)
 #pragma warning(disable:4355)
 #endif
 #endif
 
 
-// On Intel / NVIDIA setups prefer the NVIDIA GPU
+// Prefer the high-performance GPU on switchable GPU systems
 extern "C" {
 extern "C" {
-    __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+    __declspec(dllexport) DWORD NvOptimusEnablement = 1;
+    __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
 }
 }
 
 
 // Fix missing define in MinGW headers
 // Fix missing define in MinGW headers
@@ -2389,7 +2390,7 @@ bool Graphics::OpenWindow(int width, int height, bool resizable, bool borderless
 
 
     if (!impl_->window_)
     if (!impl_->window_)
     {
     {
-        LOGERROR("Could not create window");
+        LOGERRORF("Could not create window, root cause: '%s'", SDL_GetError());
         return false;
         return false;
     }
     }
 
 

+ 9 - 1
Source/Atomic/Graphics/Direct3D9/D3D9Texture.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -234,6 +234,14 @@ unsigned Texture::GetRowDataSize(int width) const
     }
     }
 }
 }
 
 
+unsigned Texture::GetComponents() const
+{
+    if (!width_ || IsCompressed())
+        return 0;
+    else
+        return GetRowDataSize(width_) / width_;
+}
+
 void Texture::SetParameters(XMLFile* file)
 void Texture::SetParameters(XMLFile* file)
 {
 {
     if (!file)
     if (!file)

+ 2 - 0
Source/Atomic/Graphics/Direct3D9/D3D9Texture.h

@@ -101,6 +101,8 @@ public:
     unsigned GetDataSize(int width, int height, int depth) const;
     unsigned GetDataSize(int width, int height, int depth) const;
     /// Return data size in bytes for a pixel or block row.
     /// Return data size in bytes for a pixel or block row.
     unsigned GetRowDataSize(int width) const;
     unsigned GetRowDataSize(int width) const;
+    /// Return number of image components required to receive pixel data from GetData(), or 0 for compressed images.
+    unsigned GetComponents() const;
 
 
     /// Set additional parameters from an XML file.
     /// Set additional parameters from an XML file.
     void SetParameters(XMLFile* xml);
     void SetParameters(XMLFile* xml);

+ 41 - 6
Source/Atomic/Graphics/Direct3D9/D3D9Texture2D.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -464,12 +464,40 @@ bool Texture2D::GetData(unsigned level, void* dest) const
     d3dRect.right = levelWidth;
     d3dRect.right = levelWidth;
     d3dRect.bottom = levelHeight;
     d3dRect.bottom = levelHeight;
     
     
-    if (FAILED(((IDirect3DTexture9*)object_)->LockRect(level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
+    IDirect3DSurface9* offscreenSurface = 0;
+    // Need to use a offscreen surface & GetRenderTargetData() for rendertargets
+    if (renderSurface_)
     {
     {
-        LOGERROR("Could not lock texture");
-        return false;
+        if (level != 0)
+        {
+            LOGERROR("Can only get mip level 0 data from a rendertarget");
+            return false;
+        }
+
+        IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
+        device->CreateOffscreenPlainSurface(width_, height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, 0);
+        if (!offscreenSurface)
+        {
+            LOGERROR("Could not create surface for getting rendertarget data");
+            return false;
+        }
+        device->GetRenderTargetData((IDirect3DSurface9*)renderSurface_->GetSurface(), offscreenSurface);
+        if (FAILED(offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
+        {
+            LOGERROR("Could not lock surface for getting rendertarget data");
+            offscreenSurface->Release();
+            return false;
+        }
     }
     }
-    
+    else
+    {
+        if (FAILED(((IDirect3DTexture9*)object_)->LockRect(level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
+        {
+            LOGERROR("Could not lock texture");
+            return false;
+        }
+    }
+
     int height = levelHeight;
     int height = levelHeight;
     if (IsCompressed())
     if (IsCompressed())
         height = (height + 3) >> 2;
         height = (height + 3) >> 2;
@@ -517,7 +545,14 @@ bool Texture2D::GetData(unsigned level, void* dest) const
         break;
         break;
     }
     }
     
     
-    ((IDirect3DTexture9*)object_)->UnlockRect(level);
+    if (offscreenSurface)
+    {
+        offscreenSurface->UnlockRect();
+        offscreenSurface->Release();
+    }
+    else
+        ((IDirect3DTexture9*)object_)->UnlockRect(level);
+
     return true;
     return true;
 }
 }
 
 

+ 40 - 5
Source/Atomic/Graphics/Direct3D9/D3D9TextureCube.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -669,10 +669,38 @@ bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
     d3dRect.right = levelWidth;
     d3dRect.right = levelWidth;
     d3dRect.bottom = levelHeight;
     d3dRect.bottom = levelHeight;
     
     
-    if (FAILED(((IDirect3DCubeTexture9*)object_)->LockRect((D3DCUBEMAP_FACES)face, level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
+    IDirect3DSurface9* offscreenSurface = 0;
+    // Need to use a offscreen surface & GetRenderTargetData() for rendertargets
+    if (renderSurfaces_[face])
     {
     {
-        LOGERROR("Could not lock texture");
-        return false;
+        if (level != 0)
+        {
+            LOGERROR("Can only get mip level 0 data from a rendertarget");
+            return false;
+        }
+
+        IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice();
+        device->CreateOffscreenPlainSurface(width_, height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, 0);
+        if (!offscreenSurface)
+        {
+            LOGERROR("Could not create surface for getting rendertarget data");
+            return false;
+        }
+        device->GetRenderTargetData((IDirect3DSurface9*)renderSurfaces_[face]->GetSurface(), offscreenSurface);
+        if (FAILED(offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
+        {
+            LOGERROR("Could not lock surface for getting rendertarget data");
+            offscreenSurface->Release();
+            return false;
+        }
+    }
+    else
+    {
+        if (FAILED(((IDirect3DCubeTexture9*)object_)->LockRect((D3DCUBEMAP_FACES)face, level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY)))
+        {
+            LOGERROR("Could not lock texture");
+            return false;
+        }
     }
     }
     
     
     int height = levelHeight;
     int height = levelHeight;
@@ -722,7 +750,14 @@ bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
         break;
         break;
     }
     }
     
     
-    ((IDirect3DCubeTexture9*)object_)->UnlockRect((D3DCUBEMAP_FACES)face, level);
+    if (offscreenSurface)
+    {
+        offscreenSurface->UnlockRect();
+        offscreenSurface->Release();
+    }
+    else
+        ((IDirect3DCubeTexture9*)object_)->UnlockRect((D3DCUBEMAP_FACES)face, level);
+    
     return true;
     return true;
 }
 }
 
 

+ 6 - 5
Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp

@@ -58,10 +58,11 @@
 #endif
 #endif
 
 
 #ifdef WIN32
 #ifdef WIN32
-// On Intel / NVIDIA setups prefer the NVIDIA GPU
+// Prefer the high-performance GPU on switchable GPU systems
 #include <windows.h>
 #include <windows.h>
 extern "C" {
 extern "C" {
-    __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+    __declspec(dllexport) DWORD NvOptimusEnablement = 1;
+    __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
 }
 }
 #endif
 #endif
 
 
@@ -497,7 +498,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
                 }
                 }
                 else
                 else
                 {
                 {
-                    LOGERROR("Could not open window");
+                    LOGERRORF("Could not create window, root cause: '%s'", SDL_GetError());
                     return false;
                     return false;
                 }
                 }
             }
             }
@@ -2438,7 +2439,7 @@ void Graphics::Restore()
         
         
         if (!impl_->context_)
         if (!impl_->context_)
         {
         {
-            LOGERROR("Could not create OpenGL context");
+            LOGERRORF("Could not create OpenGL context, root cause '%s'", SDL_GetError());
             return;
             return;
         }
         }
 
 
@@ -2450,7 +2451,7 @@ void Graphics::Restore()
         GLenum err = glewInit();
         GLenum err = glewInit();
         if (GLEW_OK != err)
         if (GLEW_OK != err)
         {
         {
-            LOGERROR("Could not initialize OpenGL extensions");
+            LOGERRORF("Could not initialize OpenGL extensions, root cause: '%s'", glewGetErrorString(err));
             return;
             return;
         }
         }
 
 

+ 8 - 0
Source/Atomic/Graphics/OpenGL/OGLTexture.cpp

@@ -367,6 +367,14 @@ unsigned Texture::GetRowDataSize(int width) const
     }
     }
 }
 }
 
 
+unsigned Texture::GetComponents() const
+{
+    if (!width_ || IsCompressed())
+        return 0;
+    else
+        return GetRowDataSize(width_) / width_;
+}
+
 unsigned Texture::GetExternalFormat(unsigned format)
 unsigned Texture::GetExternalFormat(unsigned format)
 {
 {
     #ifndef GL_ES_VERSION_2_0
     #ifndef GL_ES_VERSION_2_0

+ 2 - 0
Source/Atomic/Graphics/OpenGL/OGLTexture.h

@@ -109,6 +109,8 @@ public:
     unsigned GetDataSize(int width, int height, int depth) const;
     unsigned GetDataSize(int width, int height, int depth) const;
     /// Return data size in bytes for a pixel or block row.
     /// Return data size in bytes for a pixel or block row.
     unsigned GetRowDataSize(int width) const;
     unsigned GetRowDataSize(int width) const;
+    /// Return number of image components required to receive pixel data from GetData(), or 0 for compressed images.
+    unsigned GetComponents() const;
     /// Return the non-internal texture format corresponding to an OpenGL internal format.
     /// Return the non-internal texture format corresponding to an OpenGL internal format.
     static unsigned GetExternalFormat(unsigned format);
     static unsigned GetExternalFormat(unsigned format);
     /// Return the data type corresponding to an OpenGL internal format.
     /// Return the data type corresponding to an OpenGL internal format.

+ 8 - 0
Source/Atomic/Graphics/RenderPath.cpp

@@ -52,6 +52,8 @@ static const char* sortModeNames[] =
     0
     0
 };
 };
 
 
+extern const char* blendModeNames[];
+
 TextureUnit ParseTextureUnitName(String name);
 TextureUnit ParseTextureUnitName(String name);
 
 
 void RenderTargetInfo::Load(const XMLElement& element)
 void RenderTargetInfo::Load(const XMLElement& element)
@@ -157,6 +159,12 @@ void RenderPathCommand::Load(const XMLElement& element)
         
         
         if (type_ == CMD_QUAD)
         if (type_ == CMD_QUAD)
         {
         {
+            if (element.HasAttribute("blend"))
+            {
+                String blend = element.GetAttributeLower("blend");
+                blendMode_ = ((BlendMode)GetStringListIndex(blend.CString(), blendModeNames, BLEND_REPLACE));
+            }
+
             XMLElement parameterElem = element.GetChild("parameter");
             XMLElement parameterElem = element.GetChild("parameter");
             while (parameterElem)
             while (parameterElem)
             {
             {

+ 7 - 4
Source/Atomic/Graphics/RenderPath.h

@@ -107,6 +107,7 @@ struct RenderPathCommand
     /// Construct.
     /// Construct.
     RenderPathCommand() :
     RenderPathCommand() :
         clearFlags_(0),
         clearFlags_(0),
+        blendMode_(BLEND_REPLACE),
         enabled_(true),
         enabled_(true),
         useFogColor_(false),
         useFogColor_(false),
         markToStencil_(false),
         markToStencil_(false),
@@ -175,14 +176,16 @@ struct RenderPathCommand
     Vector<Pair<String, CubeMapFace> > outputs_;
     Vector<Pair<String, CubeMapFace> > outputs_;
     /// Depth-stencil output name.
     /// Depth-stencil output name.
     String depthStencilName_;
     String depthStencilName_;
-    /// Clear flags.
+    /// Clear flags. Affects clear command only.
     unsigned clearFlags_;
     unsigned clearFlags_;
-    /// Clear color.
+    /// Clear color. Affects clear command only.
     Color clearColor_;
     Color clearColor_;
-    /// Clear depth.
+    /// Clear depth. Affects clear command only.
     float clearDepth_;
     float clearDepth_;
-    /// Clear stencil value.
+    /// Clear stencil value. Affects clear command only.
     unsigned clearStencil_;
     unsigned clearStencil_;
+    /// Blend mode. Affects quad command only.
+    BlendMode blendMode_;
     /// Enabled flag.
     /// Enabled flag.
     bool enabled_;
     bool enabled_;
     /// Use fog color for clearing.
     /// Use fog color for clearing.

+ 9 - 6
Source/Atomic/Graphics/View.cpp

@@ -46,12 +46,11 @@
 #include "../Graphics/TextureCube.h"
 #include "../Graphics/TextureCube.h"
 #include "../Graphics/VertexBuffer.h"
 #include "../Graphics/VertexBuffer.h"
 #include "../Graphics/View.h"
 #include "../Graphics/View.h"
+#include "../UI/UI.h"
 #include "../Core/WorkQueue.h"
 #include "../Core/WorkQueue.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
-#include "../UI/UI.h"
-
 namespace Atomic
 namespace Atomic
 {
 {
 
 
@@ -1622,11 +1621,15 @@ void View::SetRenderTargets(RenderPathCommand& command)
     unsigned index = 0;
     unsigned index = 0;
     bool useColorWrite = true;
     bool useColorWrite = true;
     bool useCustomDepth = false;
     bool useCustomDepth = false;
+    bool useViewportOutput = false;
 
 
     while (index < command.outputs_.Size())
     while (index < command.outputs_.Size())
     {
     {
         if (!command.outputs_[index].first_.Compare("viewport", false))
         if (!command.outputs_[index].first_.Compare("viewport", false))
+        {
             graphics_->SetRenderTarget(index, currentRenderTarget_);
             graphics_->SetRenderTarget(index, currentRenderTarget_);
+            useViewportOutput = true;
+        }
         else
         else
         {
         {
             Texture* texture = FindNamedTexture(command.outputs_[index].first_, true, false);
             Texture* texture = FindNamedTexture(command.outputs_[index].first_, true, false);
@@ -1671,10 +1674,10 @@ void View::SetRenderTargets(RenderPathCommand& command)
         }
         }
     }
     }
 
 
-    // When rendering to the final destination rendertarget, use the actual viewport. Otherwise texture rendertargets will be
-    // viewport-sized, so they should use their full size as the viewport
+    // When rendering to the final destination rendertarget, use the actual viewport. Otherwise texture rendertargets should use 
+    // their full size as the viewport
     IntVector2 rtSizeNow = graphics_->GetRenderTargetDimensions();
     IntVector2 rtSizeNow = graphics_->GetRenderTargetDimensions();
-    IntRect viewport = (currentRenderTarget_ == renderTarget_) ? viewRect_ : IntRect(0, 0, rtSizeNow.x_,
+    IntRect viewport = (useViewportOutput && currentRenderTarget_ == renderTarget_) ? viewRect_ : IntRect(0, 0, rtSizeNow.x_,
         rtSizeNow.y_);
         rtSizeNow.y_);
     
     
     if (!useCustomDepth)
     if (!useCustomDepth)
@@ -1773,7 +1776,7 @@ void View::RenderQuad(RenderPathCommand& command)
         graphics_->SetShaderParameter(offsetsName, Vector2(pixelUVOffset.x_ / width, pixelUVOffset.y_ / height));
         graphics_->SetShaderParameter(offsetsName, Vector2(pixelUVOffset.x_ / width, pixelUVOffset.y_ / height));
     }
     }
     
     
-    graphics_->SetBlendMode(BLEND_REPLACE);
+    graphics_->SetBlendMode(command.blendMode_);
     graphics_->SetDepthTest(CMP_ALWAYS);
     graphics_->SetDepthTest(CMP_ALWAYS);
     graphics_->SetDepthWrite(false);
     graphics_->SetDepthWrite(false);
     graphics_->SetFillMode(FILL_SOLID);
     graphics_->SetFillMode(FILL_SOLID);

+ 113 - 123
Source/Atomic/Navigation/CrowdAgent.cpp

@@ -45,8 +45,8 @@ namespace Atomic
 extern const char* NAVIGATION_CATEGORY;
 extern const char* NAVIGATION_CATEGORY;
 
 
 static const unsigned DEFAULT_AGENT_NAVIGATION_FILTER_TYPE = 0;
 static const unsigned DEFAULT_AGENT_NAVIGATION_FILTER_TYPE = 0;
-static const float DEFAULT_AGENT_MAX_SPEED = 5.0f;
-static const float DEFAULT_AGENT_MAX_ACCEL = 3.6f;
+static const float DEFAULT_AGENT_MAX_SPEED = 0.f;
+static const float DEFAULT_AGENT_MAX_ACCEL = 0.f;
 static const NavigationQuality DEFAULT_AGENT_AVOIDANCE_QUALITY = NAVIGATIONQUALITY_HIGH;
 static const NavigationQuality DEFAULT_AGENT_AVOIDANCE_QUALITY = NAVIGATIONQUALITY_HIGH;
 static const NavigationPushiness DEFAULT_AGENT_NAVIGATION_PUSHINESS = PUSHINESS_MEDIUM;
 static const NavigationPushiness DEFAULT_AGENT_NAVIGATION_PUSHINESS = PUSHINESS_MEDIUM;
 
 
@@ -86,6 +86,7 @@ CrowdAgent::CrowdAgent(Context* context) :
 
 
 CrowdAgent::~CrowdAgent()
 CrowdAgent::~CrowdAgent()
 {
 {
+    RemoveAgentFromCrowd();
 }
 }
 
 
 void CrowdAgent::RegisterObject(Context* context)
 void CrowdAgent::RegisterObject(Context* context)
@@ -96,6 +97,7 @@ void CrowdAgent::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE("Max Speed", GetMaxSpeed, SetMaxSpeed, float, DEFAULT_AGENT_MAX_SPEED, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Max Speed", GetMaxSpeed, SetMaxSpeed, float, DEFAULT_AGENT_MAX_SPEED, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Radius", GetRadius, SetRadius, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Radius", GetRadius, SetRadius, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Height", GetHeight, SetHeight, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Height", GetHeight, SetHeight, float, 0.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Target Position", GetTargetPosition, SetMoveTarget, Vector3, Vector3::ZERO, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Navigation Filter", GetNavigationFilterType, SetNavigationFilterType, unsigned, DEFAULT_AGENT_NAVIGATION_FILTER_TYPE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Navigation Filter", GetNavigationFilterType, SetNavigationFilterType, unsigned, DEFAULT_AGENT_NAVIGATION_FILTER_TYPE, AM_DEFAULT);
     ENUM_ACCESSOR_ATTRIBUTE("Navigation Pushiness", GetNavigationPushiness, SetNavigationPushiness, NavigationPushiness, crowdAgentPushinessNames, PUSHINESS_LOW, AM_DEFAULT);
     ENUM_ACCESSOR_ATTRIBUTE("Navigation Pushiness", GetNavigationPushiness, SetNavigationPushiness, NavigationPushiness, crowdAgentPushinessNames, PUSHINESS_LOW, AM_DEFAULT);
     ENUM_ACCESSOR_ATTRIBUTE("Navigation Quality", GetNavigationQuality, SetNavigationQuality, NavigationQuality, crowdAgentAvoidanceQualityNames, NAVIGATIONQUALITY_LOW, AM_DEFAULT);
     ENUM_ACCESSOR_ATTRIBUTE("Navigation Quality", GetNavigationQuality, SetNavigationQuality, NavigationQuality, crowdAgentAvoidanceQualityNames, NAVIGATIONQUALITY_LOW, AM_DEFAULT);
@@ -117,6 +119,8 @@ void CrowdAgent::OnNodeSet(Node* node)
 
 
         node->AddListener(this);
         node->AddListener(this);
     }
     }
+    else
+        RemoveAgentFromCrowd();
 }
 }
 
 
 void CrowdAgent::OnSetEnabled()
 void CrowdAgent::OnSetEnabled()
@@ -147,17 +151,22 @@ void CrowdAgent::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
         const Vector3 pos = GetPosition();
         const Vector3 pos = GetPosition();
         const Vector3 vel = GetActualVelocity();
         const Vector3 vel = GetActualVelocity();
         const Vector3 desiredVel = GetDesiredVelocity();
         const Vector3 desiredVel = GetDesiredVelocity();
-        const Vector3 agentHeightVec(0, height_ * 0.5f, 0);
+        const Vector3 agentHeightVec(0, height_, 0);
 
 
-        debug->AddLine(pos, pos + vel, Color::GREEN, depthTest);
-        debug->AddLine(pos + agentHeightVec, pos + desiredVel + agentHeightVec, Color::RED, depthTest);
-        debug->AddCylinder(pos, radius_, height_, Color::GREEN, depthTest);
+        debug->AddLine(pos + 0.5f * agentHeightVec, pos + vel + 0.5f * agentHeightVec, Color::GREEN, depthTest);
+        debug->AddLine(pos + 0.25f * agentHeightVec, pos + desiredVel + 0.25f * agentHeightVec, Color::RED, depthTest);
+        debug->AddCylinder(pos, radius_, height_, HasArrived() ? Color::GREEN : Color::WHITE, depthTest);
     }
     }
 }
 }
 
 
+const dtCrowdAgent* CrowdAgent::GetDetourCrowdAgent() const
+{
+    return crowdManager_ && inCrowd_ ? crowdManager_->GetCrowdAgent(agentCrowdId_) : 0;
+}
+
 void CrowdAgent::AddAgentToCrowd()
 void CrowdAgent::AddAgentToCrowd()
 {
 {
-    if (!crowdManager_ || !crowdManager_->crowd_)
+    if (!crowdManager_ || !crowdManager_->crowd_ || !node_)
         return;
         return;
 
 
     PROFILE(AddAgentToCrowd);
     PROFILE(AddAgentToCrowd);
@@ -172,8 +181,6 @@ void CrowdAgent::AddAgentToCrowd()
             LOGERROR("AddAgentToCrowd: Could not add agent to crowd");
             LOGERROR("AddAgentToCrowd: Could not add agent to crowd");
             return;
             return;
         }
         }
-        dtCrowdAgentParams& params = crowdManager_->GetCrowd()->getEditableAgent(agentCrowdId_)->params;
-        params.userData = this;
         crowdManager_->UpdateAgentNavigationQuality(this, navQuality_);
         crowdManager_->UpdateAgentNavigationQuality(this, navQuality_);
         crowdManager_->UpdateAgentPushiness(this, navPushiness_);
         crowdManager_->UpdateAgentPushiness(this, navPushiness_);
         previousAgentState_ = GetAgentState();
         previousAgentState_ = GetAgentState();
@@ -195,6 +202,9 @@ void CrowdAgent::AddAgentToCrowd()
             previousAgentState_ = GetAgentState();
             previousAgentState_ = GetAgentState();
             previousTargetState_ = GetTargetState();
             previousTargetState_ = GetTargetState();
         }
         }
+
+        // Save the initial position to prevent CrowdAgentReposition event being triggered unnecessarily
+        previousPosition_ = GetPosition();
     }
     }
 }
 }
 
 
@@ -221,42 +231,45 @@ void CrowdAgent::SetNavigationFilterType(unsigned filterType)
     }
     }
 }
 }
 
 
-bool CrowdAgent::SetMoveTarget(const Vector3& position)
+void CrowdAgent::SetMoveTarget(const Vector3& position)
 {
 {
-    if (crowdManager_ && !inCrowd_)
-        AddAgentToCrowd();
-    if (crowdManager_ && inCrowd_)
-    {
+    if (crowdManager_) {
+        if (!inCrowd_)
+            AddAgentToCrowd();
         targetPosition_ = position;
         targetPosition_ = position;
         if (crowdManager_->SetAgentTarget(this, position, targetRef_))
         if (crowdManager_->SetAgentTarget(this, position, targetRef_))
-        {
             MarkNetworkUpdate();
             MarkNetworkUpdate();
-            return true;
-        }
     }
     }
-    return false;
 }
 }
 
 
-bool CrowdAgent::SetMoveVelocity(const Vector3& velocity)
+void CrowdAgent::ResetMoveTarget()
 {
 {
-    if (crowdManager_ && inCrowd_)
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent && agent->active)
     {
     {
-        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
-        if (agent && agent->active)
-        {
-            crowdManager_->GetCrowd()->requestMoveVelocity(agentCrowdId_, velocity.Data());
-            MarkNetworkUpdate();
-        }
+        targetPosition_ = Vector3::ZERO;
+        crowdManager_->GetCrowd()->resetMoveTarget(agentCrowdId_);
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdAgent::SetMoveVelocity(const Vector3& velocity)
+{
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent && agent->active)
+    {
+        crowdManager_->GetCrowd()->requestMoveVelocity(agentCrowdId_, velocity.Data());
+        MarkNetworkUpdate();
     }
     }
-    return false;
 }
 }
 
 
 void CrowdAgent::SetMaxSpeed(float speed)
 void CrowdAgent::SetMaxSpeed(float speed)
 {
 {
-    maxSpeed_ = speed;
-    if(crowdManager_ && inCrowd_)
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent)
     {
     {
-        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        maxSpeed_ = speed;
+        dtCrowdAgentParams params = agent->params;
         params.maxSpeed = speed;
         params.maxSpeed = speed;
         crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
         crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
@@ -265,10 +278,11 @@ void CrowdAgent::SetMaxSpeed(float speed)
 
 
 void CrowdAgent::SetMaxAccel(float accel)
 void CrowdAgent::SetMaxAccel(float accel)
 {
 {
-    maxAccel_ = accel;
-    if(crowdManager_ && inCrowd_)
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent)
     {
     {
-        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        maxAccel_ = accel;
+        dtCrowdAgentParams params = agent->params;
         params.maxAcceleration = accel;
         params.maxAcceleration = accel;
         crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
         crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
@@ -277,10 +291,11 @@ void CrowdAgent::SetMaxAccel(float accel)
 
 
 void CrowdAgent::SetRadius(float radius)
 void CrowdAgent::SetRadius(float radius)
 {
 {
-    radius_ = radius;
-    if (crowdManager_ && inCrowd_)
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent)
     {
     {
-        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        radius_ = radius;
+        dtCrowdAgentParams params = agent->params;
         params.radius = radius;
         params.radius = radius;
         crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
         crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
@@ -289,10 +304,11 @@ void CrowdAgent::SetRadius(float radius)
 
 
 void CrowdAgent::SetHeight(float height)
 void CrowdAgent::SetHeight(float height)
 {
 {
-    height_ = height;
-    if (crowdManager_ && inCrowd_)
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent)
     {
     {
-        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        height_ = height;
+        dtCrowdAgentParams params = agent->params;
         params.height = height;
         params.height = height;
         crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
         crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
@@ -301,9 +317,10 @@ void CrowdAgent::SetHeight(float height)
 
 
 void CrowdAgent::SetNavigationQuality(NavigationQuality val)
 void CrowdAgent::SetNavigationQuality(NavigationQuality val)
 {
 {
-    navQuality_ = val;
-    if(crowdManager_ && inCrowd_)
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent)
     {
     {
+        navQuality_ = val;
         crowdManager_->UpdateAgentNavigationQuality(this, navQuality_);
         crowdManager_->UpdateAgentNavigationQuality(this, navQuality_);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
@@ -311,9 +328,10 @@ void CrowdAgent::SetNavigationQuality(NavigationQuality val)
 
 
 void CrowdAgent::SetNavigationPushiness(NavigationPushiness val)
 void CrowdAgent::SetNavigationPushiness(NavigationPushiness val)
 {
 {
-    navPushiness_ = val;
-    if(crowdManager_ && inCrowd_)
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent)
     {
     {
+        navPushiness_ = val;
         crowdManager_->UpdateAgentPushiness(this, navPushiness_);
         crowdManager_->UpdateAgentPushiness(this, navPushiness_);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
@@ -321,75 +339,41 @@ void CrowdAgent::SetNavigationPushiness(NavigationPushiness val)
 
 
 Vector3 CrowdAgent::GetPosition() const
 Vector3 CrowdAgent::GetPosition() const
 {
 {
-    if (crowdManager_ && inCrowd_)
-    {
-        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
-        if (agent && agent->active)
-            return Vector3(agent->npos);
-    }
-    return node_->GetPosition();
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    return agent && agent->active ? Vector3(agent->npos) : node_->GetPosition();
 }
 }
 
 
 Vector3 CrowdAgent::GetDesiredVelocity() const
 Vector3 CrowdAgent::GetDesiredVelocity() const
 {
 {
-    if (crowdManager_ && inCrowd_)
-    {
-        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
-        if (agent && agent->active)
-            return Vector3(agent->dvel);
-    }
-    return Vector3::ZERO;
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    return agent && agent->active ? Vector3(agent->dvel) : Vector3::ZERO;
 }
 }
 
 
 Vector3 CrowdAgent::GetActualVelocity() const
 Vector3 CrowdAgent::GetActualVelocity() const
 {
 {
-    if (crowdManager_ && inCrowd_)
-    {
-        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
-        if (agent && agent->active)
-            return Vector3(agent->vel);
-    }
-    return Vector3::ZERO;
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    return agent && agent->active ? Vector3(agent->vel) : Vector3::ZERO;
 }
 }
 
 
 Atomic::CrowdAgentState CrowdAgent::GetAgentState() const
 Atomic::CrowdAgentState CrowdAgent::GetAgentState() const
 {
 {
-    if (crowdManager_ && inCrowd_)
-    {
-        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
-        if (!agent || !agent->active)
-            return CROWD_AGENT_INVALID;
-        return (CrowdAgentState)agent->state;
-    }
-    return CROWD_AGENT_INVALID;
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    return agent && agent->active ? (CrowdAgentState)agent->state : CROWD_AGENT_INVALID;
 }
 }
 
 
 Atomic::CrowdTargetState CrowdAgent::GetTargetState() const
 Atomic::CrowdTargetState CrowdAgent::GetTargetState() const
 {
 {
-    if (crowdManager_ && inCrowd_)
-    {
-        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
-        if (!agent || !agent->active)
-            return CROWD_AGENT_TARGET_NONE;
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    return agent && agent->active ? (CrowdTargetState)agent->targetState : CROWD_AGENT_TARGET_NONE;
+}
 
 
-        // Determine if we've arrived at the target
-        if (agent->targetState == DT_CROWDAGENT_TARGET_VALID)
-        {
-            if (agent->ncorners)
-            {
-                // Is the agent at the end of its path?
-                if (agent->cornerFlags[agent->ncorners - 1] & DT_STRAIGHTPATH_END)
-                {
-                    // Within its own radius of the goal?
-                    if (dtVdist2D(agent->npos, &agent->cornerVerts[(agent->ncorners - 1) * 3]) <= agent->params.radius)
-                        return CROWD_AGENT_TARGET_ARRIVED;
-            
-                }
-            }
-        }
-        return (CrowdTargetState)agent->targetState;
-    }
-    return CROWD_AGENT_TARGET_NONE;
+bool CrowdAgent::HasArrived() const
+{
+    // Is the agent at or near the end of its path?
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    return agent && agent->active && (!agent->ncorners ||
+        (agent->cornerFlags[agent->ncorners - 1] & DT_STRAIGHTPATH_END &&
+            Equals(dtVdist2D(agent->npos, &agent->cornerVerts[(agent->ncorners - 1) * 3]), 0.f)));
 }
 }
 
 
 void CrowdAgent::SetUpdateNodePosition(bool unodepos)
 void CrowdAgent::SetUpdateNodePosition(bool unodepos)
@@ -398,23 +382,29 @@ void CrowdAgent::SetUpdateNodePosition(bool unodepos)
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
-void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newDirection)
+void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newVel)
 {
 {
     if (node_)
     if (node_)
     {
     {
         // Notify parent node of the reposition
         // Notify parent node of the reposition
-        VariantMap& map = GetContext()->GetEventDataMap();
-        map[CrowdAgentReposition::P_NODE] = GetNode();
-        map[CrowdAgentReposition::P_CROWD_AGENT] = this;
-        map[CrowdAgentReposition::P_POSITION] = newPos;
-        map[CrowdAgentReposition::P_VELOCITY] = GetActualVelocity();
-        SendEvent(E_CROWD_AGENT_REPOSITION, map);
-        
-        if (updateNodePosition_)
+        if (newPos != previousPosition_)
         {
         {
-            ignoreTransformChanges_ = true;
-            node_->SetPosition(newPos);
-            ignoreTransformChanges_ = false;
+            previousPosition_ = newPos;
+
+            VariantMap& map = GetContext()->GetEventDataMap();
+            map[CrowdAgentReposition::P_NODE] = GetNode();
+            map[CrowdAgentReposition::P_CROWD_AGENT] = this;
+            map[CrowdAgentReposition::P_POSITION] = newPos;
+            map[CrowdAgentReposition::P_VELOCITY] = newVel;
+            map[CrowdAgentReposition::P_ARRIVED] = HasArrived();
+            SendEvent(E_CROWD_AGENT_REPOSITION, map);
+
+            if (updateNodePosition_)
+            {
+                ignoreTransformChanges_ = true;
+                node_->SetPosition(newPos);
+                ignoreTransformChanges_ = false;
+            }
         }
         }
 
 
         // Send a notification event if we've reached the destination
         // Send a notification event if we've reached the destination
@@ -428,7 +418,7 @@ void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& ne
             map[CrowdAgentStateChanged::P_CROWD_TARGET_STATE] = newTargetState;
             map[CrowdAgentStateChanged::P_CROWD_TARGET_STATE] = newTargetState;
             map[CrowdAgentStateChanged::P_CROWD_AGENT_STATE] = newAgentState;
             map[CrowdAgentStateChanged::P_CROWD_AGENT_STATE] = newAgentState;
             map[CrowdAgentStateChanged::P_POSITION] = newPos;
             map[CrowdAgentStateChanged::P_POSITION] = newPos;
-            map[CrowdAgentStateChanged::P_VELOCITY] = GetActualVelocity();
+            map[CrowdAgentStateChanged::P_VELOCITY] = newVel;
             SendEvent(E_CROWD_AGENT_STATE_CHANGED, map);
             SendEvent(E_CROWD_AGENT_STATE_CHANGED, map);
 
 
             // Send a failure event if either state is a failed status
             // Send a failure event if either state is a failed status
@@ -440,7 +430,7 @@ void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& ne
                 map[CrowdAgentFailure::P_CROWD_TARGET_STATE] = newTargetState;
                 map[CrowdAgentFailure::P_CROWD_TARGET_STATE] = newTargetState;
                 map[CrowdAgentFailure::P_CROWD_AGENT_STATE] = newAgentState;
                 map[CrowdAgentFailure::P_CROWD_AGENT_STATE] = newAgentState;
                 map[CrowdAgentFailure::P_POSITION] = newPos;
                 map[CrowdAgentFailure::P_POSITION] = newPos;
-                map[CrowdAgentFailure::P_VELOCITY] = GetActualVelocity();
+                map[CrowdAgentFailure::P_VELOCITY] = newVel;
                 SendEvent(E_CROWD_AGENT_FAILURE, map);
                 SendEvent(E_CROWD_AGENT_FAILURE, map);
             }
             }
 
 
@@ -453,11 +443,10 @@ void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& ne
 
 
 PODVector<unsigned char> CrowdAgent::GetAgentDataAttr() const
 PODVector<unsigned char> CrowdAgent::GetAgentDataAttr() const
 {
 {
-    if (!inCrowd_ || !crowdManager_ || !IsEnabled())
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (!agent)
         return Variant::emptyBuffer;
         return Variant::emptyBuffer;
-    dtCrowd* crowd = crowdManager_->GetCrowd();
-    const dtCrowdAgent* agent = crowd->getAgent(agentCrowdId_);
-    
+
     // Reading it back in isn't this simple, see SetAgentDataAttr
     // Reading it back in isn't this simple, see SetAgentDataAttr
     VectorBuffer ret;
     VectorBuffer ret;
     ret.Write(agent, sizeof(dtCrowdAgent));
     ret.Write(agent, sizeof(dtCrowdAgent));
@@ -467,12 +456,14 @@ PODVector<unsigned char> CrowdAgent::GetAgentDataAttr() const
 
 
 void CrowdAgent::SetAgentDataAttr(const PODVector<unsigned char>& value)
 void CrowdAgent::SetAgentDataAttr(const PODVector<unsigned char>& value)
 {
 {
-    if (value.Empty() || !inCrowd_ || !crowdManager_ || !IsEnabled())
+    if (value.Empty())
+        return;
+
+    dtCrowdAgent* agent = const_cast<dtCrowdAgent*>(GetDetourCrowdAgent());
+    if (!agent)
         return;
         return;
 
 
     MemoryBuffer buffer(value);
     MemoryBuffer buffer(value);
-    dtCrowd* crowd = crowdManager_->GetCrowd();
-    dtCrowdAgent* agent = crowd->getEditableAgent(agentCrowdId_);
 
 
     // Path corridor is tricky
     // Path corridor is tricky
     char corridorData[sizeof(dtPathCorridor)];
     char corridorData[sizeof(dtPathCorridor)];
@@ -491,19 +482,18 @@ void CrowdAgent::SetAgentDataAttr(const PODVector<unsigned char>& value)
 
 
 void CrowdAgent::OnMarkedDirty(Node* node)
 void CrowdAgent::OnMarkedDirty(Node* node)
 {
 {
-    if (inCrowd_ && crowdManager_ && !ignoreTransformChanges_ && IsEnabledEffective())
+    if (!ignoreTransformChanges_ && IsEnabledEffective())
     {
     {
-        dtCrowdAgent* agt = crowdManager_->GetCrowd()->getEditableAgent(agentCrowdId_);
-        if (agt)
+        dtCrowdAgent* agent = const_cast<dtCrowdAgent*>(GetDetourCrowdAgent());
+        if (agent)
         {
         {
-            memcpy(agt->npos, node->GetWorldPosition().Data(), sizeof(float) * 3);
+            memcpy(agent->npos, node->GetWorldPosition().Data(), sizeof(float) * 3);
 
 
             // If the node has been externally altered, provide the opportunity for DetourCrowd to reevaluate the crowd agent
             // If the node has been externally altered, provide the opportunity for DetourCrowd to reevaluate the crowd agent
-            if (agt->state == CROWD_AGENT_INVALID)
-                agt->state = CROWD_AGENT_READY;
+            if (agent->state == CROWD_AGENT_INVALID)
+                agent->state = CROWD_AGENT_READY;
         }
         }
     }
     }
 }
 }
 
 
-
 }
 }

+ 19 - 12
Source/Atomic/Navigation/CrowdAgent.h

@@ -34,17 +34,16 @@ enum CrowdTargetState
     CROWD_AGENT_TARGET_FAILED,
     CROWD_AGENT_TARGET_FAILED,
     CROWD_AGENT_TARGET_VALID,
     CROWD_AGENT_TARGET_VALID,
     CROWD_AGENT_TARGET_REQUESTING,
     CROWD_AGENT_TARGET_REQUESTING,
-    CROWD_AGENT_TARGET_WAITINGFORPATH,
     CROWD_AGENT_TARGET_WAITINGFORQUEUE,
     CROWD_AGENT_TARGET_WAITINGFORQUEUE,
-    CROWD_AGENT_TARGET_VELOCITY,
-    CROWD_AGENT_TARGET_ARRIVED
+    CROWD_AGENT_TARGET_WAITINGFORPATH,
+    CROWD_AGENT_TARGET_VELOCITY
 };
 };
 
 
 enum CrowdAgentState
 enum CrowdAgentState
 {
 {
-    CROWD_AGENT_INVALID = 0,	///< The agent is not in a valid state.
-    CROWD_AGENT_READY,			///< The agent is traversing a normal navigation mesh polygon.
-    CROWD_AGENT_TRAVERSINGLINK	///< The agent is traversing an off-mesh connection.
+    CROWD_AGENT_INVALID = 0,      ///< The agent is not in a valid state.
+    CROWD_AGENT_READY,            ///< The agent is traversing a normal navigation mesh polygon.
+    CROWD_AGENT_TRAVERSINGLINK    ///< The agent is traversing an off-mesh connection.
 };
 };
 
 
 /// DetourCrowd Agent, requires a DetourCrowdManager in the scene. Agent's radius and height is set through the navigation mesh.
 /// DetourCrowd Agent, requires a DetourCrowdManager in the scene. Agent's radius and height is set through the navigation mesh.
@@ -71,9 +70,11 @@ public:
     /// Set the navigation filter type the agent will use.
     /// Set the navigation filter type the agent will use.
     void SetNavigationFilterType(unsigned filterTypeID);
     void SetNavigationFilterType(unsigned filterTypeID);
     /// Submit a new move request for this agent.
     /// Submit a new move request for this agent.
-    bool SetMoveTarget(const Vector3& position);
+    void SetMoveTarget(const Vector3& position);
+    /// Reset any request for the specified agent.
+    void ResetMoveTarget();
     /// Submit a new move velocity request for this agent.
     /// Submit a new move velocity request for this agent.
-    bool SetMoveVelocity(const Vector3& velocity);
+    void SetMoveVelocity(const Vector3& velocity);
     /// Update the node position. When set to false, the node position should be updated by other means (e.g. using Physics) in response to the E_CROWD_AGENT_REPOSITION event.
     /// Update the node position. When set to false, the node position should be updated by other means (e.g. using Physics) in response to the E_CROWD_AGENT_REPOSITION event.
     void SetUpdateNodePosition(bool unodepos);
     void SetUpdateNodePosition(bool unodepos);
     /// Set the agent's max acceleration.
     /// Set the agent's max acceleration.
@@ -110,7 +111,7 @@ public:
     /// Get the agent's max velocity.
     /// Get the agent's max velocity.
     float GetMaxSpeed() const { return maxSpeed_; }
     float GetMaxSpeed() const { return maxSpeed_; }
     /// Get the agent's max acceleration.
     /// Get the agent's max acceleration.
-    float GetMaxAccel()	const { return maxAccel_; }
+    float GetMaxAccel() const { return maxAccel_; }
     /// Get the agent's radius.
     /// Get the agent's radius.
     float GetRadius() const { return radius_; }
     float GetRadius() const { return radius_; }
     /// Get the agent's height.
     /// Get the agent's height.
@@ -119,6 +120,8 @@ public:
     NavigationQuality GetNavigationQuality() const {return navQuality_; }
     NavigationQuality GetNavigationQuality() const {return navQuality_; }
     /// Get the agent's navigation pushiness.
     /// Get the agent's navigation pushiness.
     NavigationPushiness GetNavigationPushiness() const {return navPushiness_; }
     NavigationPushiness GetNavigationPushiness() const {return navPushiness_; }
+    /// Return true when the agent has arrived at its target.
+    bool HasArrived() const;
 
 
     /// Get serialized data of internal state.
     /// Get serialized data of internal state.
     PODVector<unsigned char> GetAgentDataAttr() const;
     PODVector<unsigned char> GetAgentDataAttr() const;
@@ -127,11 +130,13 @@ public:
 
 
 protected:
 protected:
     /// Update the nodes position if updateNodePosition is set. Is called in DetourCrowdManager::Update().
     /// Update the nodes position if updateNodePosition is set. Is called in DetourCrowdManager::Update().
-    virtual void OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newDirection);
+    virtual void OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newVel);
     /// Handle node being assigned.
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
     virtual void OnNodeSet(Node* node);
     /// \todo Handle node transform being dirtied.
     /// \todo Handle node transform being dirtied.
     virtual void OnMarkedDirty(Node* node);
     virtual void OnMarkedDirty(Node* node);
+    /// Get internal Detour crowd agent.
+    const dtCrowdAgent* GetDetourCrowdAgent() const;
 private:
 private:
     /// Create or re-add.
     /// Create or re-add.
     void AddAgentToCrowd();
     void AddAgentToCrowd();
@@ -144,9 +149,9 @@ private:
     /// DetourCrowd reference to this agent.
     /// DetourCrowd reference to this agent.
     int agentCrowdId_;
     int agentCrowdId_;
     /// Reference to poly closest to requested target position.
     /// Reference to poly closest to requested target position.
-    unsigned int targetRef_;         
+    unsigned int targetRef_;
     /// Actual target position, closest to that requested.
     /// Actual target position, closest to that requested.
-    Vector3 targetPosition_;   
+    Vector3 targetPosition_;
     /// Flag indicating the node's position should be updated by Detour crowd manager.
     /// Flag indicating the node's position should be updated by Detour crowd manager.
     bool updateNodePosition_;
     bool updateNodePosition_;
     /// Agent's max acceleration.
     /// Agent's max acceleration.
@@ -163,6 +168,8 @@ private:
     NavigationQuality navQuality_;
     NavigationQuality navQuality_;
     /// Agent's Navigation Pushiness.
     /// Agent's Navigation Pushiness.
     NavigationPushiness navPushiness_;
     NavigationPushiness navPushiness_;
+    /// Agent's previous position used to check for position changes.
+    Vector3 previousPosition_;
     /// Agent's previous target state used to check for state changes.
     /// Agent's previous target state used to check for state changes.
     CrowdTargetState previousTargetState_;
     CrowdTargetState previousTargetState_;
     /// Agent's previous agent state used to check for state changes.
     /// Agent's previous agent state used to check for state changes.

+ 66 - 16
Source/Atomic/Navigation/DetourCrowdManager.cpp

@@ -46,7 +46,7 @@
 
 
 namespace Atomic
 namespace Atomic
 {
 {
-    
+
 extern const char* NAVIGATION_CATEGORY;
 extern const char* NAVIGATION_CATEGORY;
 
 
 static const unsigned DEFAULT_MAX_AGENTS = 512;
 static const unsigned DEFAULT_MAX_AGENTS = 512;
@@ -72,7 +72,7 @@ DetourCrowdManager::~DetourCrowdManager()
 void DetourCrowdManager::RegisterObject(Context* context)
 void DetourCrowdManager::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<DetourCrowdManager>(NAVIGATION_CATEGORY);
     context->RegisterFactory<DetourCrowdManager>(NAVIGATION_CATEGORY);
-    
+
     ACCESSOR_ATTRIBUTE("Max Agents", GetMaxAgents, SetMaxAgents, unsigned, DEFAULT_MAX_AGENTS, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Max Agents", GetMaxAgents, SetMaxAgents, unsigned, DEFAULT_MAX_AGENTS, AM_DEFAULT);
 }
 }
 
 
@@ -117,9 +117,44 @@ void DetourCrowdManager::SetMaxAgents(unsigned agentCt)
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
-NavigationMesh* DetourCrowdManager::GetNavigationMesh()
+void DetourCrowdManager::SetCrowdTarget(const Vector3& position, int startId, int endId)
+{
+    startId = Max(0, startId);
+    endId = Clamp(endId, startId, agents_.Size() - 1);
+    Vector3 moveTarget(position);
+    for (int i = startId; i <= endId; ++i)
+    {
+        // Skip agent that does not have acceleration
+        if (agents_[i]->GetMaxAccel() > 0.f)
+        {
+            agents_[i]->SetMoveTarget(moveTarget);
+            // FIXME: Should reimplement this using event callback, i.e. it should be application-specific to decide what is the desired crowd formation when they reach the target
+            if (navigationMesh_)
+                moveTarget = navigationMesh_->FindNearestPoint(position + Vector3(Random(-4.5f, 4.5f), 0.0f, Random(-4.5f, 4.5f)), Vector3(1.0f, 1.0f, 1.0f));
+        }
+    }
+}
+
+void DetourCrowdManager::ResetCrowdTarget(int startId, int endId)
+{
+    startId = Max(0, startId);
+    endId = Clamp(endId, startId, agents_.Size() - 1);
+    for (int i = startId; i <= endId; ++i)
+    {
+        if (agents_[i]->GetMaxAccel() > 0.f)
+            agents_[i]->ResetMoveTarget();
+    }
+}
+
+void DetourCrowdManager::SetCrowdVelocity(const Vector3& velocity, int startId, int endId)
 {
 {
-    return navigationMesh_.Get();
+    startId = Max(0, startId);
+    endId = Clamp(endId, startId, agents_.Size() - 1);
+    for (int i = startId; i <= endId; ++i)
+    {
+        if (agents_[i]->GetMaxAccel() > 0.f)
+            agents_[i]->SetMoveVelocity(velocity);
+    }
 }
 }
 
 
 float DetourCrowdManager::GetAreaCost(unsigned filterID, unsigned areaID) const
 float DetourCrowdManager::GetAreaCost(unsigned filterID, unsigned areaID) const
@@ -149,9 +184,17 @@ void DetourCrowdManager::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
             if (!ag->active)
             if (!ag->active)
                 continue;
                 continue;
 
 
+            // Draw CrowdAgent shape (from its radius & height)
+            CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(ag->params.userData);
+            crowdAgent->DrawDebugGeometry(debug, depthTest);
+
+            // Draw move target if any
+            if (crowdAgent->GetTargetState() == CROWD_AGENT_TARGET_NONE)
+                continue;
+
             Color color(0.6f, 0.2f, 0.2f, 1.0f);
             Color color(0.6f, 0.2f, 0.2f, 1.0f);
 
 
-            // Render line to target:
+            // Draw line to target
             Vector3 pos1(ag->npos[0], ag->npos[1], ag->npos[2]);
             Vector3 pos1(ag->npos[0], ag->npos[1], ag->npos[2]);
             Vector3 pos2;
             Vector3 pos2;
             for (int i = 0; i < ag->ncorners; ++i)
             for (int i = 0; i < ag->ncorners; ++i)
@@ -167,12 +210,23 @@ void DetourCrowdManager::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
             pos2.z_ = ag->targetPos[2];
             pos2.z_ = ag->targetPos[2];
             debug->AddLine(pos1, pos2, color, depthTest);
             debug->AddLine(pos1, pos2, color, depthTest);
 
 
-            // Target circle
+            // Draw target circle
             debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
             debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
         }
         }
     }
     }
 }
 }
 
 
+void DetourCrowdManager::DrawDebugGeometry(bool depthTest)
+{
+    Scene* scene = GetScene();
+    if (scene)
+    {
+        DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
+        if (debug)
+            DrawDebugGeometry(debug, depthTest);
+    }
+}
+
 bool DetourCrowdManager::CreateCrowd()
 bool DetourCrowdManager::CreateCrowd()
 {
 {
     if (!navigationMesh_ || !navigationMesh_->navMesh_)
     if (!navigationMesh_ || !navigationMesh_->navMesh_)
@@ -231,6 +285,7 @@ int DetourCrowdManager::AddAgent(CrowdAgent* agent, const Vector3& pos)
     if (!crowd_ || navigationMesh_.Expired())
     if (!crowd_ || navigationMesh_.Expired())
         return -1;
         return -1;
     dtCrowdAgentParams params;
     dtCrowdAgentParams params;
+    params.userData = agent;
     if (agent->radius_ <= 0.0f)
     if (agent->radius_ <= 0.0f)
         agent->radius_ = navigationMesh_->GetAgentRadius();
         agent->radius_ = navigationMesh_->GetAgentRadius();
     params.radius = agent->radius_;
     params.radius = agent->radius_;
@@ -258,7 +313,7 @@ int DetourCrowdManager::AddAgent(CrowdAgent* agent, const Vector3& pos)
         crowd_->getFilter(agent->filterType_),
         crowd_->getFilter(agent->filterType_),
         &polyRef,
         &polyRef,
         nearestPos);
         nearestPos);
-    
+
     const int agentID = crowd_->addAgent(nearestPos, &params);
     const int agentID = crowd_->addAgent(nearestPos, &params);
     if (agentID != -1)
     if (agentID != -1)
         agents_.Push(agent);
         agents_.Push(agent);
@@ -403,12 +458,12 @@ void DetourCrowdManager::Update(float delta)
         return;
         return;
 
 
     PROFILE(UpdateCrowd);
     PROFILE(UpdateCrowd);
-    
+
     crowd_->update(delta, agentDebug_);
     crowd_->update(delta, agentDebug_);
 
 
     memset(&agentBuffer_[0], 0, maxAgents_ * sizeof(dtCrowdAgent*));
     memset(&agentBuffer_[0], 0, maxAgents_ * sizeof(dtCrowdAgent*));
     const int count = crowd_->getActiveAgents(&agentBuffer_[0], maxAgents_);
     const int count = crowd_->getActiveAgents(&agentBuffer_[0], maxAgents_);
-    
+
     {
     {
         PROFILE(ApplyCrowdUpdates);
         PROFILE(ApplyCrowdUpdates);
         for (int i = 0; i < count; i++)
         for (int i = 0; i < count; i++)
@@ -416,7 +471,7 @@ void DetourCrowdManager::Update(float delta)
             dtCrowdAgent* agent = agentBuffer_[i];
             dtCrowdAgent* agent = agentBuffer_[i];
             if (agent)
             if (agent)
             {
             {
-                CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(agent->params.userData);    
+                CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(agent->params.userData);
                 if (crowdAgent)
                 if (crowdAgent)
                     crowdAgent->OnCrowdAgentReposition(Vector3(agent->npos), Vector3(agent->vel));
                     crowdAgent->OnCrowdAgentReposition(Vector3(agent->npos), Vector3(agent->vel));
             }
             }
@@ -429,11 +484,6 @@ const dtCrowdAgent* DetourCrowdManager::GetCrowdAgent(int agent)
     return crowd_ ? crowd_->getAgent(agent) : 0;
     return crowd_ ? crowd_->getAgent(agent) : 0;
 }
 }
 
 
-dtCrowd* DetourCrowdManager::GetCrowd()
-{
-    return crowd_;
-}
-
 void DetourCrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
 void DetourCrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace SceneSubsystemUpdate;
     using namespace SceneSubsystemUpdate;
@@ -472,7 +522,7 @@ void DetourCrowdManager::OnNodeSet(Node* node)
     {
     {
         SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
         SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
         SubscribeToEvent(node, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
         SubscribeToEvent(node, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
-        
+
         NavigationMesh* mesh = GetScene()->GetComponent<NavigationMesh>();
         NavigationMesh* mesh = GetScene()->GetComponent<NavigationMesh>();
         if (!mesh)
         if (!mesh)
             mesh = GetScene()->GetComponent<DynamicNavigationMesh>();
             mesh = GetScene()->GetComponent<DynamicNavigationMesh>();

+ 14 - 6
Source/Atomic/Navigation/DetourCrowdManager.h

@@ -43,7 +43,7 @@ enum NavigationQuality
 
 
 enum NavigationPushiness
 enum NavigationPushiness
 {
 {
-    PUSHINESS_LOW,
+    PUSHINESS_LOW = 0,
     PUSHINESS_MEDIUM,
     PUSHINESS_MEDIUM,
     PUSHINESS_HIGH
     PUSHINESS_HIGH
 };
 };
@@ -54,7 +54,7 @@ class ATOMIC_API DetourCrowdManager : public Component
 {
 {
     OBJECT(DetourCrowdManager);
     OBJECT(DetourCrowdManager);
     friend class CrowdAgent;
     friend class CrowdAgent;
-              
+
 public:
 public:
     /// Construct.
     /// Construct.
     DetourCrowdManager(Context* context);
     DetourCrowdManager(Context* context);
@@ -69,9 +69,15 @@ public:
     void SetAreaCost(unsigned filterTypeID, unsigned areaID, float weight);
     void SetAreaCost(unsigned filterTypeID, unsigned areaID, float weight);
     /// Set the maximum number of agents.
     /// Set the maximum number of agents.
     void SetMaxAgents(unsigned agentCt);
     void SetMaxAgents(unsigned agentCt);
+    /// Set the crowd move target. The move target is applied to all crowd agents within the id range, excluding crowd agent which does not have acceleration.
+    void SetCrowdTarget(const Vector3& position, int startId = 0, int endId = M_MAX_INT);
+    /// Reset the crowd move target to all crowd agents within the id range, excluding crowd agent which does not have acceleration.
+    void ResetCrowdTarget(int startId = 0, int endId = M_MAX_INT);
+    /// Set the crowd move velocity. The move velocity is applied to all crowd agents within the id range, excluding crowd agent which does not have acceleration.
+    void SetCrowdVelocity(const Vector3& velocity, int startId = 0, int endId = M_MAX_INT);
 
 
     /// Get the Navigation mesh assigned to the crowd.
     /// Get the Navigation mesh assigned to the crowd.
-    NavigationMesh* GetNavigationMesh();
+    NavigationMesh* GetNavigationMesh() const { return navigationMesh_; }
     /// Get the cost of an area-type for the specified navigation filter type.
     /// Get the cost of an area-type for the specified navigation filter type.
     float GetAreaCost(unsigned filterTypeID, unsigned areaID) const;
     float GetAreaCost(unsigned filterTypeID, unsigned areaID) const;
     /// Get the maximum number of agents.
     /// Get the maximum number of agents.
@@ -81,8 +87,10 @@ public:
 
 
     /// Draw the agents' pathing debug data.
     /// Draw the agents' pathing debug data.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+    /// Add debug geometry to the debug renderer.
+    void DrawDebugGeometry(bool depthTest);
     /// Get the currently included agents.
     /// Get the currently included agents.
-    PODVector<CrowdAgent*> GetActiveAgents() const { return agents_; }
+    const PODVector<CrowdAgent*>& GetActiveAgents() const { return agents_; }
     /// Create detour crowd component for the specified navigation mesh.
     /// Create detour crowd component for the specified navigation mesh.
     bool CreateCrowd();
     bool CreateCrowd();
 
 
@@ -113,7 +121,7 @@ protected:
     /// Get the detour crowd agent.
     /// Get the detour crowd agent.
     const dtCrowdAgent* GetCrowdAgent(int agent);
     const dtCrowdAgent* GetCrowdAgent(int agent);
     /// Get the internal detour crowd component.
     /// Get the internal detour crowd component.
-    dtCrowd* GetCrowd();
+    dtCrowd* GetCrowd() const { return crowd_; }
 
 
 private:
 private:
     /// Handle the scene subsystem update event.
     /// Handle the scene subsystem update event.
@@ -126,7 +134,7 @@ private:
     /// NavigationMesh for which the crowd was created.
     /// NavigationMesh for which the crowd was created.
     WeakPtr<NavigationMesh> navigationMesh_;
     WeakPtr<NavigationMesh> navigationMesh_;
     /// Max agents for the crowd.
     /// Max agents for the crowd.
-    unsigned maxAgents_;    
+    unsigned maxAgents_;
     /// Internal debug information.
     /// Internal debug information.
     dtCrowdAgentDebugInfo* agentDebug_;
     dtCrowdAgentDebugInfo* agentDebug_;
     /// Container for fetching agents from DetourCrowd during update.
     /// Container for fetching agents from DetourCrowd during update.

+ 84 - 16
Source/Atomic/Navigation/DynamicNavigationMesh.cpp

@@ -24,15 +24,17 @@
 
 
 #include "../Math/BoundingBox.h"
 #include "../Math/BoundingBox.h"
 #include "../Core/Context.h"
 #include "../Core/Context.h"
+#include "../Navigation/CrowdAgent.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/MemoryBuffer.h"
+#include "../Navigation/NavArea.h"
 #include "../Navigation/NavBuildData.h"
 #include "../Navigation/NavBuildData.h"
 #include "../Navigation/NavigationEvents.h"
 #include "../Navigation/NavigationEvents.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
+#include "../Navigation/Obstacle.h"
 #include "../Navigation/OffMeshConnection.h"
 #include "../Navigation/OffMeshConnection.h"
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
-#include "../Navigation/Obstacle.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
 #include "../Scene/SceneEvents.h"
 #include "../Scene/SceneEvents.h"
 
 
@@ -53,7 +55,7 @@
 
 
 namespace Atomic
 namespace Atomic
 {
 {
-    
+
 extern const char* NAVIGATION_CATEGORY;
 extern const char* NAVIGATION_CATEGORY;
 
 
 static const int DEFAULT_MAX_OBSTACLES = 1024;
 static const int DEFAULT_MAX_OBSTACLES = 1024;
@@ -113,7 +115,7 @@ struct MeshProcess : public dtTileCacheMeshProcess
         BoundingBox bounds;
         BoundingBox bounds;
         rcVcopy(&bounds.min_.x_, params->bmin);
         rcVcopy(&bounds.min_.x_, params->bmin);
         rcVcopy(&bounds.max_.x_, params->bmin);
         rcVcopy(&bounds.max_.x_, params->bmin);
-        
+
         // collect off-mesh connections
         // collect off-mesh connections
         PODVector<OffMeshConnection*> offMeshConnections = owner_->CollectOffMeshConnections(bounds);
         PODVector<OffMeshConnection*> offMeshConnections = owner_->CollectOffMeshConnections(bounds);
 
 
@@ -177,7 +179,7 @@ struct LinearAllocator : public dtTileCacheAlloc
 
 
     void resize(const int cap)
     void resize(const int cap)
     {
     {
-        if (buffer) 
+        if (buffer)
             dtFree(buffer);
             dtFree(buffer);
         buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
         buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
         capacity = cap;
         capacity = cap;
@@ -209,10 +211,11 @@ struct LinearAllocator : public dtTileCacheAlloc
 DynamicNavigationMesh::DynamicNavigationMesh(Context* context) :
 DynamicNavigationMesh::DynamicNavigationMesh(Context* context) :
     NavigationMesh(context),
     NavigationMesh(context),
     tileCache_(0),
     tileCache_(0),
-    maxObstacles_(1024)
+    maxObstacles_(1024),
+    drawObstacles_(false)
 {
 {
     //64 is the largest tile-size that DetourTileCache will tolerate without silently failing
     //64 is the largest tile-size that DetourTileCache will tolerate without silently failing
-    tileSize_ = 64; 
+    tileSize_ = 64;
     partitionType_ = NAVMESH_PARTITION_MONOTONE;
     partitionType_ = NAVMESH_PARTITION_MONOTONE;
     allocator_ = new LinearAllocator(32000); //32kb to start
     allocator_ = new LinearAllocator(32000); //32kb to start
     compressor_ = new TileCompressor();
     compressor_ = new TileCompressor();
@@ -235,7 +238,8 @@ void DynamicNavigationMesh::RegisterObject(Context* context)
     context->RegisterFactory<DynamicNavigationMesh>(NAVIGATION_CATEGORY);
     context->RegisterFactory<DynamicNavigationMesh>(NAVIGATION_CATEGORY);
 
 
     COPY_BASE_ATTRIBUTES(NavigationMesh);
     COPY_BASE_ATTRIBUTES(NavigationMesh);
-    ATTRIBUTE("Max Obstacles", unsigned, maxObstacles_, DEFAULT_MAX_OBSTACLES, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Max Obstacles", GetMaxObstacles, SetMaxObstacles, unsigned, DEFAULT_MAX_OBSTACLES, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Draw Obstacles", GetDrawObstacles, SetDrawObstacles, bool, false, AM_DEFAULT);
 }
 }
 
 
 bool DynamicNavigationMesh::Build()
 bool DynamicNavigationMesh::Build()
@@ -361,7 +365,7 @@ bool DynamicNavigationMesh::Build()
             for (int x = 0; x < numTilesX_; ++x)
             for (int x = 0; x < numTilesX_; ++x)
                 tileCache_->buildNavMeshTilesAt(x, z, navMesh_);
                 tileCache_->buildNavMeshTilesAt(x, z, navMesh_);
         }
         }
-        
+
         // For a full build it's necessary to update the nav mesh
         // For a full build it's necessary to update the nav mesh
         // not doing so will cause dependent components to crash, like DetourCrowdManager
         // not doing so will cause dependent components to crash, like DetourCrowdManager
         tileCache_->update(0, navMesh_);
         tileCache_->update(0, navMesh_);
@@ -475,7 +479,7 @@ void DynamicNavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTe
             // Get the layers from the tile-cache
             // Get the layers from the tile-cache
             const dtMeshTile* tiles[TILECACHE_MAXLAYERS];
             const dtMeshTile* tiles[TILECACHE_MAXLAYERS];
             int tileCount = navMesh->getTilesAt(x, z, tiles, TILECACHE_MAXLAYERS);
             int tileCount = navMesh->getTilesAt(x, z, tiles, TILECACHE_MAXLAYERS);
-            for (int i = 0; i < tileCount; ++i) 
+            for (int i = 0; i < tileCount; ++i)
             {
             {
                 const dtMeshTile* tile = tiles[i];
                 const dtMeshTile* tile = tiles[i];
                 if (!tile)
                 if (!tile)
@@ -497,12 +501,66 @@ void DynamicNavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTe
             }
             }
         }
         }
     }
     }
+
+    Scene* scene = GetScene();
+    if (scene)
+    {
+        // Draw Obstacle components
+        if (drawObstacles_)
+        {
+            PODVector<Node*> obstacles;
+            scene->GetChildrenWithComponent<Obstacle>(obstacles, true);
+            for (unsigned i = 0; i < obstacles.Size(); ++i)
+            {
+                Obstacle* obstacle = obstacles[i]->GetComponent<Obstacle>();
+                if (obstacle && obstacle->IsEnabledEffective())
+                    obstacle->DrawDebugGeometry(debug, depthTest);
+            }
+        }
+
+        // Draw OffMeshConnection components
+        if (drawOffMeshConnections_)
+        {
+            PODVector<Node*> connections;
+            scene->GetChildrenWithComponent<OffMeshConnection>(connections, true);
+            for (unsigned i = 0; i < connections.Size(); ++i)
+            {
+                OffMeshConnection* connection = connections[i]->GetComponent<OffMeshConnection>();
+                if (connection && connection->IsEnabledEffective())
+                    connection->DrawDebugGeometry(debug, depthTest);
+            }
+        }
+
+        // Draw NavArea components
+        if (drawNavAreas_)
+        {
+            PODVector<Node*> areas;
+            scene->GetChildrenWithComponent<NavArea>(areas, true);
+            for (unsigned i = 0; i < areas.Size(); ++i)
+            {
+                NavArea* area = areas[i]->GetComponent<NavArea>();
+                if (area && area->IsEnabledEffective())
+                    area->DrawDebugGeometry(debug, depthTest);
+            }
+        }
+    }
+}
+
+void DynamicNavigationMesh::DrawDebugGeometry(bool depthTest)
+{
+    Scene* scene = GetScene();
+    if (scene)
+    {
+        DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
+        if (debug)
+            DrawDebugGeometry(debug, depthTest);
+    }
 }
 }
 
 
 void DynamicNavigationMesh::SetNavigationDataAttr(const PODVector<unsigned char>& value)
 void DynamicNavigationMesh::SetNavigationDataAttr(const PODVector<unsigned char>& value)
 {
 {
     ReleaseNavigationMesh();
     ReleaseNavigationMesh();
-        
+
     if (value.Empty())
     if (value.Empty())
         return;
         return;
 
 
@@ -684,7 +742,7 @@ int DynamicNavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryLis
     rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
     rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
         triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
         triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
     rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
     rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
-    
+
     rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
     rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
     rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
     rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
 
 
@@ -732,7 +790,7 @@ int DynamicNavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryLis
             return 0;
             return 0;
         }
         }
     }
     }
-    
+
     build.heightFieldLayers_ = rcAllocHeightfieldLayerSet();
     build.heightFieldLayers_ = rcAllocHeightfieldLayerSet();
     if (!build.heightFieldLayers_)
     if (!build.heightFieldLayers_)
     {
     {
@@ -838,6 +896,12 @@ void DynamicNavigationMesh::AddObstacle(Obstacle* obstacle, bool silent)
         Vector3 obsPos = obstacle->GetNode()->GetWorldPosition();
         Vector3 obsPos = obstacle->GetNode()->GetWorldPosition();
         rcVcopy(pos, &obsPos.x_);
         rcVcopy(pos, &obsPos.x_);
         dtObstacleRef refHolder;
         dtObstacleRef refHolder;
+
+        // Because dtTileCache doesn't process obstacle requests while updating tiles 
+        // it's necessary update until sufficient request space is available
+        while (tileCache_->isObstacleQueueFull())
+            tileCache_->update(1, navMesh_);
+
         if (dtStatusFailed(tileCache_->addObstacle(pos, obstacle->GetRadius(), obstacle->GetHeight(), &refHolder)))
         if (dtStatusFailed(tileCache_->addObstacle(pos, obstacle->GetRadius(), obstacle->GetHeight(), &refHolder)))
         {
         {
             LOGERROR("Failed to add obstacle");
             LOGERROR("Failed to add obstacle");
@@ -845,8 +909,7 @@ void DynamicNavigationMesh::AddObstacle(Obstacle* obstacle, bool silent)
         }
         }
         obstacle->obstacleId_ = refHolder;
         obstacle->obstacleId_ = refHolder;
         assert(refHolder > 0);
         assert(refHolder > 0);
-        tileCache_->update(1, navMesh_);
-        
+
         if (!silent)
         if (!silent)
         {
         {
             using namespace NavigationObstacleAdded;
             using namespace NavigationObstacleAdded;
@@ -874,15 +937,20 @@ void DynamicNavigationMesh::RemoveObstacle(Obstacle* obstacle, bool silent)
 {
 {
     if (tileCache_ && obstacle->obstacleId_ > 0)
     if (tileCache_ && obstacle->obstacleId_ > 0)
     {
     {
+        // Because dtTileCache doesn't process obstacle requests while updating tiles 
+        // it's necessary update until sufficient request space is available
+        while (tileCache_->isObstacleQueueFull())
+            tileCache_->update(1, navMesh_);
+
         if (dtStatusFailed(tileCache_->removeObstacle(obstacle->obstacleId_)))
         if (dtStatusFailed(tileCache_->removeObstacle(obstacle->obstacleId_)))
         {
         {
             LOGERROR("Failed to remove obstacle");
             LOGERROR("Failed to remove obstacle");
             return;
             return;
         }
         }
+        obstacle->obstacleId_ = 0;
         // Require a node in order to send an event
         // Require a node in order to send an event
         if (!silent && obstacle->GetNode())
         if (!silent && obstacle->GetNode())
         {
         {
-            obstacle->obstacleId_ = 0;
             using namespace NavigationObstacleRemoved;
             using namespace NavigationObstacleRemoved;
             VariantMap& eventData = GetContext()->GetEventDataMap();
             VariantMap& eventData = GetContext()->GetEventDataMap();
             eventData[P_NODE] = obstacle->GetNode();
             eventData[P_NODE] = obstacle->GetNode();
@@ -890,7 +958,7 @@ void DynamicNavigationMesh::RemoveObstacle(Obstacle* obstacle, bool silent)
             eventData[P_POSITION] = obstacle->GetNode()->GetWorldPosition();
             eventData[P_POSITION] = obstacle->GetNode()->GetWorldPosition();
             eventData[P_RADIUS] = obstacle->GetRadius();
             eventData[P_RADIUS] = obstacle->GetRadius();
             eventData[P_HEIGHT] = obstacle->GetHeight();
             eventData[P_HEIGHT] = obstacle->GetHeight();
-            SendEvent(E_NAVIGATION_OBSTACLE_ADDED, eventData);
+            SendEvent(E_NAVIGATION_OBSTACLE_REMOVED, eventData);
         }
         }
     }
     }
 }
 }

+ 14 - 0
Source/Atomic/Navigation/DynamicNavigationMesh.h

@@ -59,12 +59,24 @@ public:
     virtual bool Build(const BoundingBox& boundingBox);
     virtual bool Build(const BoundingBox& boundingBox);
     /// Visualize the component as debug geometry.
     /// Visualize the component as debug geometry.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+    /// Add debug geometry to the debug renderer.
+    void DrawDebugGeometry(bool depthTest);
 
 
     /// Set navigation data attribute.
     /// Set navigation data attribute.
     virtual void SetNavigationDataAttr(const PODVector<unsigned char>& value);
     virtual void SetNavigationDataAttr(const PODVector<unsigned char>& value);
     /// Return navigation data attribute.
     /// Return navigation data attribute.
     virtual PODVector<unsigned char> GetNavigationDataAttr() const;
     virtual PODVector<unsigned char> GetNavigationDataAttr() const;
 
 
+    /// Set the maximum number of obstacles allowed.
+    void SetMaxObstacles(unsigned maxObstacles) { maxObstacles_ = maxObstacles; }
+    /// Return the maximum number of obstacles allowed.
+    unsigned GetMaxObstacles() const { return maxObstacles_; }
+
+    /// Draw debug geometry for Obstacles.
+    void SetDrawObstacles(bool enable) { drawObstacles_ = enable; }
+    /// Return whether to draw Obstacles.
+    bool GetDrawObstacles() const { return drawObstacles_; }
+
 protected:
 protected:
     struct TileCacheData;
     struct TileCacheData;
 
 
@@ -101,6 +113,8 @@ private:
     dtTileCacheMeshProcess* meshProcessor_;
     dtTileCacheMeshProcess* meshProcessor_;
     /// Maximum number of obstacle objects allowed.
     /// Maximum number of obstacle objects allowed.
     unsigned maxObstacles_;
     unsigned maxObstacles_;
+    /// Debug draw Obstacles.
+    bool drawObstacles_;
 };
 };
 
 
 }
 }

+ 1 - 0
Source/Atomic/Navigation/NavigationEvents.h

@@ -50,6 +50,7 @@ EVENT(E_CROWD_AGENT_REPOSITION, CrowdAgentReposition)
     PARAM(P_CROWD_AGENT, CrowdAgent); // CrowdAgent pointer
     PARAM(P_CROWD_AGENT, CrowdAgent); // CrowdAgent pointer
     PARAM(P_POSITION, Position); // Vector3
     PARAM(P_POSITION, Position); // Vector3
     PARAM(P_VELOCITY, Velocity); // Vector3
     PARAM(P_VELOCITY, Velocity); // Vector3
+    PARAM(P_ARRIVED, Arrived); // bool
 }
 }
 
 
 /// Crowd agent's internal state has become invalidated.
 /// Crowd agent's internal state has become invalidated.

+ 41 - 4
Source/Atomic/Navigation/NavigationMesh.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -123,7 +123,9 @@ NavigationMesh::NavigationMesh(Context* context) :
     numTilesX_(0),
     numTilesX_(0),
     numTilesZ_(0),
     numTilesZ_(0),
     partitionType_(NAVMESH_PARTITION_WATERSHED),
     partitionType_(NAVMESH_PARTITION_WATERSHED),
-    keepInterResults_(false)
+    keepInterResults_(false),
+    drawOffMeshConnections_(false),
+    drawNavAreas_(false)
 {
 {
 }
 }
 
 
@@ -158,6 +160,8 @@ void NavigationMesh::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE("Bounding Box Padding", GetPadding, SetPadding, Vector3, Vector3::ONE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Bounding Box Padding", GetPadding, SetPadding, Vector3, Vector3::ONE, AM_DEFAULT);
     MIXED_ACCESSOR_ATTRIBUTE("Navigation Data", GetNavigationDataAttr, SetNavigationDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
     MIXED_ACCESSOR_ATTRIBUTE("Navigation Data", GetNavigationDataAttr, SetNavigationDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
     ENUM_ACCESSOR_ATTRIBUTE("Partition Type", GetPartitionType, SetPartitionType, NavmeshPartitionType, navmeshPartitionTypeNames, NAVMESH_PARTITION_WATERSHED, AM_DEFAULT);
     ENUM_ACCESSOR_ATTRIBUTE("Partition Type", GetPartitionType, SetPartitionType, NavmeshPartitionType, navmeshPartitionTypeNames, NAVMESH_PARTITION_WATERSHED, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Draw OffMeshConnections", GetDrawOffMeshConnections, SetDrawOffMeshConnections, bool, false, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Draw NavAreas", GetDrawNavAreas, SetDrawNavAreas, bool, false, AM_DEFAULT);
 }
 }
 
 
 void NavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 void NavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
@@ -195,6 +199,36 @@ void NavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
             }
             }
         }
         }
     }
     }
+
+    Scene* scene = GetScene();
+    if (scene)
+    {
+        // Draw OffMeshConnection components
+        if (drawOffMeshConnections_)
+        {
+            PODVector<Node*> connections;
+            scene->GetChildrenWithComponent<OffMeshConnection>(connections, true);
+            for (unsigned i = 0; i < connections.Size(); ++i)
+            {
+                OffMeshConnection* connection = connections[i]->GetComponent<OffMeshConnection>();
+                if (connection && connection->IsEnabledEffective())
+                    connection->DrawDebugGeometry(debug, depthTest);
+            }
+        }
+
+        // Draw NavArea components
+        if (drawNavAreas_)
+        {
+            PODVector<Node*> areas;
+            scene->GetChildrenWithComponent<NavArea>(areas, true);
+            for (unsigned i = 0; i < areas.Size(); ++i)
+            {
+                NavArea* area = areas[i]->GetComponent<NavArea>();
+                if (area && area->IsEnabledEffective())
+                    area->DrawDebugGeometry(debug, depthTest);
+            }
+        }
+    }
 }
 }
 
 
 void NavigationMesh::SetMeshName(const String& newName)
 void NavigationMesh::SetMeshName(const String& newName)
@@ -499,7 +533,7 @@ void NavigationMesh::FindPath(PODVector<Vector3>& dest, const Vector3& start, co
 
 
     Vector3 localStart = inverse * start;
     Vector3 localStart = inverse * start;
     Vector3 localEnd = inverse * end;
     Vector3 localEnd = inverse * end;
-    
+
     dtPolyRef startRef;
     dtPolyRef startRef;
     dtPolyRef endRef;
     dtPolyRef endRef;
     navMeshQuery_->findNearestPoly(&localStart.x_, &extents.x_, queryFilter_, &startRef, 0);
     navMeshQuery_->findNearestPoly(&localStart.x_, &extents.x_, queryFilter_, &startRef, 0);
@@ -803,6 +837,9 @@ void NavigationMesh::CollectGeometries(Vector<NavigationGeometryInfo>& geometryL
     // Make sure nodes are not included twice
     // Make sure nodes are not included twice
     if (processedNodes.Contains(node))
     if (processedNodes.Contains(node))
         return;
         return;
+    // Exclude obstacles from consideration
+    if (node->HasComponent<Obstacle>())
+        return;
     processedNodes.Insert(node);
     processedNodes.Insert(node);
 
 
     Matrix3x4 inverse = node_->GetWorldTransform().Inverse();
     Matrix3x4 inverse = node_->GetWorldTransform().Inverse();
@@ -1118,7 +1155,7 @@ bool NavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int
     rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
     rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
         triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
         triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
     rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
     rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
-    
+
     rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
     rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
     rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
     rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
 
 

+ 18 - 3
Source/Atomic/Navigation/NavigationMesh.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -45,8 +45,8 @@ namespace Atomic
 
 
 enum NavmeshPartitionType
 enum NavmeshPartitionType
 {
 {
-    NAVMESH_PARTITION_WATERSHED,
-    NAVMESH_PARTITION_MONOTONE,
+    NAVMESH_PARTITION_WATERSHED = 0,
+    NAVMESH_PARTITION_MONOTONE
 };
 };
 
 
 class Geometry;
 class Geometry;
@@ -188,6 +188,16 @@ public:
     /// Return navigation data attribute.
     /// Return navigation data attribute.
     virtual PODVector<unsigned char> GetNavigationDataAttr() const;
     virtual PODVector<unsigned char> GetNavigationDataAttr() const;
 
 
+    /// Draw debug geometry for OffMeshConnection components.
+    void SetDrawOffMeshConnections(bool enable) { drawOffMeshConnections_ = enable; }
+    /// Return whether to draw OffMeshConnection components.
+    bool GetDrawOffMeshConnections() const { return drawOffMeshConnections_; }
+
+    /// Draw debug geometry for NavArea components.
+    void SetDrawNavAreas(bool enable) { drawNavAreas_ = enable; }
+    /// Return whether to draw NavArea components.
+    bool GetDrawNavAreas() const { return drawNavAreas_; }
+
 protected:
 protected:
     /// Collect geometry from under Navigable components.
     /// Collect geometry from under Navigable components.
     void CollectGeometries(Vector<NavigationGeometryInfo>& geometryList);
     void CollectGeometries(Vector<NavigationGeometryInfo>& geometryList);
@@ -255,6 +265,11 @@ protected:
     bool keepInterResults_;
     bool keepInterResults_;
     /// Internal build resources for creating the navmesh.
     /// Internal build resources for creating the navmesh.
     HashMap<Pair<int, int>, NavBuildData*> builds_;
     HashMap<Pair<int, int>, NavBuildData*> builds_;
+
+    /// Debug draw OffMeshConnection components.
+    bool drawOffMeshConnections_;
+    /// Debug draw NavArea components.
+    bool drawNavAreas_;
 };
 };
 
 
 /// Register Navigation library objects.
 /// Register Navigation library objects.

+ 5 - 0
Source/Atomic/Navigation/Obstacle.cpp

@@ -99,6 +99,11 @@ void Obstacle::OnNodeSet(Node* node)
         if (ownerMesh_)
         if (ownerMesh_)
             ownerMesh_->AddObstacle(this);
             ownerMesh_->AddObstacle(this);
     }
     }
+    else
+    {
+        if (obstacleId_ > 0 && ownerMesh_)
+            ownerMesh_->RemoveObstacle(this);
+    }
 }
 }
 
 
 void Obstacle::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 void Obstacle::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)

+ 9 - 7
Source/Atomic/Navigation/OffMeshConnection.cpp

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -35,14 +35,16 @@ extern const char* NAVIGATION_CATEGORY;
 
 
 static const float DEFAULT_RADIUS = 1.0f;
 static const float DEFAULT_RADIUS = 1.0f;
 static const unsigned DEFAULT_MASK_FLAG = 1;
 static const unsigned DEFAULT_MASK_FLAG = 1;
-static const unsigned DEFAULT_AREA = 0;
+static const unsigned DEFAULT_AREA = 1;
 
 
 OffMeshConnection::OffMeshConnection(Context* context) :
 OffMeshConnection::OffMeshConnection(Context* context) :
     Component(context),
     Component(context),
     endPointID_(0),
     endPointID_(0),
     radius_(DEFAULT_RADIUS),
     radius_(DEFAULT_RADIUS),
     bidirectional_(true),
     bidirectional_(true),
-    endPointDirty_(false)
+    endPointDirty_(false),
+    mask_(DEFAULT_MASK_FLAG),
+    areaId_(DEFAULT_AREA)
 {
 {
 }
 }
 
 
@@ -53,7 +55,7 @@ OffMeshConnection::~OffMeshConnection()
 void OffMeshConnection::RegisterObject(Context* context)
 void OffMeshConnection::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<OffMeshConnection>(NAVIGATION_CATEGORY);
     context->RegisterFactory<OffMeshConnection>(NAVIGATION_CATEGORY);
-    
+
     ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ATTRIBUTE("Endpoint NodeID", int, endPointID_, 0, AM_DEFAULT | AM_NODEID);
     ATTRIBUTE("Endpoint NodeID", int, endPointID_, 0, AM_DEFAULT | AM_NODEID);
     ATTRIBUTE("Radius", float, radius_, DEFAULT_RADIUS, AM_DEFAULT);
     ATTRIBUTE("Radius", float, radius_, DEFAULT_RADIUS, AM_DEFAULT);
@@ -65,7 +67,7 @@ void OffMeshConnection::RegisterObject(Context* context)
 void OffMeshConnection::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 void OffMeshConnection::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 {
 {
     Serializable::OnSetAttribute(attr, src);
     Serializable::OnSetAttribute(attr, src);
-    
+
     if (attr.offset_ == offsetof(OffMeshConnection, endPointID_))
     if (attr.offset_ == offsetof(OffMeshConnection, endPointID_))
         endPointDirty_ = true;
         endPointDirty_ = true;
 }
 }
@@ -84,10 +86,10 @@ void OffMeshConnection::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 {
 {
     if (!node_ || !endPoint_)
     if (!node_ || !endPoint_)
         return;
         return;
-    
+
     Vector3 start = node_->GetWorldPosition();
     Vector3 start = node_->GetWorldPosition();
     Vector3 end = endPoint_->GetWorldPosition();
     Vector3 end = endPoint_->GetWorldPosition();
-    
+
     debug->AddSphere(Sphere(start, radius_), Color::WHITE, depthTest);
     debug->AddSphere(Sphere(start, radius_), Color::WHITE, depthTest);
     debug->AddSphere(Sphere(end, radius_), Color::WHITE, depthTest);
     debug->AddSphere(Sphere(end, radius_), Color::WHITE, depthTest);
     debug->AddLine(start, end, Color::WHITE, depthTest);
     debug->AddLine(start, end, Color::WHITE, depthTest);

+ 6 - 6
Source/Atomic/Navigation/OffMeshConnection.h

@@ -1,5 +1,5 @@
 //
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
 // of this software and associated documentation files (the "Software"), to deal
@@ -31,7 +31,7 @@ namespace Atomic
 class ATOMIC_API OffMeshConnection : public Component
 class ATOMIC_API OffMeshConnection : public Component
 {
 {
     OBJECT(OffMeshConnection);
     OBJECT(OffMeshConnection);
-    
+
 public:
 public:
     /// Construct.
     /// Construct.
     OffMeshConnection(Context* context);
     OffMeshConnection(Context* context);
@@ -39,14 +39,14 @@ public:
     virtual ~OffMeshConnection();
     virtual ~OffMeshConnection();
     /// Register object factory.
     /// Register object factory.
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
-    
+
     /// Handle attribute write access.
     /// Handle attribute write access.
     virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
     virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
     /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
     /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
     virtual void ApplyAttributes();
     virtual void ApplyAttributes();
     /// Visualize the component as debug geometry.
     /// Visualize the component as debug geometry.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
-    
+
     /// Set endpoint node.
     /// Set endpoint node.
     void SetEndPoint(Node* node);
     void SetEndPoint(Node* node);
     /// Set radius.
     /// Set radius.
@@ -57,7 +57,7 @@ public:
     void SetMask(unsigned newMask);
     void SetMask(unsigned newMask);
     /// Sets the assigned area Id for the connection
     /// Sets the assigned area Id for the connection
     void SetAreaID(unsigned newAreaID);
     void SetAreaID(unsigned newAreaID);
-    
+
     /// Return endpoint node.
     /// Return endpoint node.
     Node* GetEndPoint() const;
     Node* GetEndPoint() const;
     /// Return radius.
     /// Return radius.
@@ -68,7 +68,7 @@ public:
     unsigned GetMask() const { return mask_; }
     unsigned GetMask() const { return mask_; }
     /// Return the user assigned area ID
     /// Return the user assigned area ID
     unsigned GetAreaID() const { return areaId_; }
     unsigned GetAreaID() const { return areaId_; }
-    
+
 private:
 private:
     /// Endpoint node.
     /// Endpoint node.
     WeakPtr<Node> endPoint_;
     WeakPtr<Node> endPoint_;

+ 18 - 2
Source/Atomic/Physics/CollisionShape.cpp

@@ -59,6 +59,7 @@ namespace Atomic
 {
 {
 
 
 static const float DEFAULT_COLLISION_MARGIN = 0.04f;
 static const float DEFAULT_COLLISION_MARGIN = 0.04f;
+static const unsigned QUANTIZE_MAX_TRIANGLES = 1000000;
 
 
 static const btVector3 WHITE(1.0f, 1.0f, 1.0f);
 static const btVector3 WHITE(1.0f, 1.0f, 1.0f);
 static const btVector3 GREEN(0.0f, 1.0f, 0.0f);
 static const btVector3 GREEN(0.0f, 1.0f, 0.0f);
@@ -85,6 +86,7 @@ public:
     TriangleMeshInterface(Model* model, unsigned lodLevel) : btTriangleIndexVertexArray()
     TriangleMeshInterface(Model* model, unsigned lodLevel) : btTriangleIndexVertexArray()
     {
     {
         unsigned numGeometries = model->GetNumGeometries();
         unsigned numGeometries = model->GetNumGeometries();
+        unsigned totalTriangles = 0;
 
 
         for (unsigned i = 0; i < numGeometries; ++i)
         for (unsigned i = 0; i < numGeometries; ++i)
         {
         {
@@ -125,13 +127,20 @@ public:
             meshIndex.m_indexType = (indexSize == sizeof(unsigned short)) ? PHY_SHORT : PHY_INTEGER;
             meshIndex.m_indexType = (indexSize == sizeof(unsigned short)) ? PHY_SHORT : PHY_INTEGER;
             meshIndex.m_vertexType = PHY_FLOAT;
             meshIndex.m_vertexType = PHY_FLOAT;
             m_indexedMeshes.push_back(meshIndex);
             m_indexedMeshes.push_back(meshIndex);
+            
+            totalTriangles += meshIndex.m_numTriangles;
         }
         }
+
+        // Bullet will not work properly with quantized AABB compression, if the triangle count is too large. Use a conservative
+        // threshold value
+        useQuantize_ = totalTriangles <= QUANTIZE_MAX_TRIANGLES;
     }
     }
 
 
     TriangleMeshInterface(CustomGeometry* custom) : btTriangleIndexVertexArray()
     TriangleMeshInterface(CustomGeometry* custom) : btTriangleIndexVertexArray()
     {
     {
         const Vector<PODVector<CustomGeometryVertex> >& srcVertices = custom->GetVertices();
         const Vector<PODVector<CustomGeometryVertex> >& srcVertices = custom->GetVertices();
         unsigned totalVertexCount = 0;
         unsigned totalVertexCount = 0;
+        unsigned totalTriangles = 0;
 
 
         for (unsigned i = 0; i < srcVertices.Size(); ++i)
         for (unsigned i = 0; i < srcVertices.Size(); ++i)
             totalVertexCount += srcVertices[i].Size();
             totalVertexCount += srcVertices[i].Size();
@@ -167,9 +176,16 @@ public:
             meshIndex.m_indexType = PHY_INTEGER;
             meshIndex.m_indexType = PHY_INTEGER;
             meshIndex.m_vertexType = PHY_FLOAT;
             meshIndex.m_vertexType = PHY_FLOAT;
             m_indexedMeshes.push_back(meshIndex);
             m_indexedMeshes.push_back(meshIndex);
+
+            totalTriangles += meshIndex.m_numTriangles;
         }
         }
+
+        useQuantize_ = totalTriangles <= QUANTIZE_MAX_TRIANGLES;
     }
     }
 
 
+    /// OK to use quantization flag.
+    bool useQuantize_;
+
 private:
 private:
     /// Shared vertex/index data used in the collision
     /// Shared vertex/index data used in the collision
     Vector<SharedArrayPtr<unsigned char> > dataArrays_;
     Vector<SharedArrayPtr<unsigned char> > dataArrays_;
@@ -181,7 +197,7 @@ TriangleMeshData::TriangleMeshData(Model* model, unsigned lodLevel) :
     infoMap_(0)
     infoMap_(0)
 {
 {
     meshInterface_ = new TriangleMeshInterface(model, lodLevel);
     meshInterface_ = new TriangleMeshInterface(model, lodLevel);
-    shape_ = new btBvhTriangleMeshShape(meshInterface_, true, true);
+    shape_ = new btBvhTriangleMeshShape(meshInterface_, meshInterface_->useQuantize_, true);
 
 
     infoMap_ = new btTriangleInfoMap();
     infoMap_ = new btTriangleInfoMap();
     btGenerateInternalEdgeInfo(shape_, infoMap_);
     btGenerateInternalEdgeInfo(shape_, infoMap_);
@@ -193,7 +209,7 @@ TriangleMeshData::TriangleMeshData(CustomGeometry* custom) :
     infoMap_(0)
     infoMap_(0)
 {
 {
     meshInterface_ = new TriangleMeshInterface(custom);
     meshInterface_ = new TriangleMeshInterface(custom);
-    shape_ = new btBvhTriangleMeshShape(meshInterface_, true, true);
+    shape_ = new btBvhTriangleMeshShape(meshInterface_, meshInterface_->useQuantize_, true);
 
 
     infoMap_ = new btTriangleInfoMap();
     infoMap_ = new btTriangleInfoMap();
     btGenerateInternalEdgeInfo(shape_, infoMap_);
     btGenerateInternalEdgeInfo(shape_, infoMap_);

+ 3 - 0
Source/ThirdParty/DetourTileCache/include/DetourTileCache.h

@@ -2,6 +2,7 @@
 #define DETOURTILECACHE_H
 #define DETOURTILECACHE_H
 
 
 #include <Detour/include/DetourStatus.h>
 #include <Detour/include/DetourStatus.h>
+// Modified by Lasse Oorni for Urho3D
 
 
 typedef unsigned int dtObstacleRef;
 typedef unsigned int dtObstacleRef;
 
 
@@ -119,6 +120,8 @@ public:
 	
 	
 	void getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const;
 	void getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const;
 	
 	
+    // Urho3D: added function to know when we have too many obstacle requests without update
+    bool isObstacleQueueFull() const { return m_nreqs >= MAX_REQUESTS; }
 
 
 	/// Encodes a tile id.
 	/// Encodes a tile id.
 	inline dtCompressedTileRef encodeTileId(unsigned int salt, unsigned int it) const
 	inline dtCompressedTileRef encodeTileId(unsigned int salt, unsigned int it) const

+ 20 - 4
Source/Tools/AssetImporter/AssetImporter.cpp

@@ -1072,18 +1072,34 @@ void BuildAndSaveAnimations(OutModel* model)
                     continue;
                     continue;
                 }
                 }
             }
             }
-            
+
+            // To export single frame animation, check if first key frame is identical to bone transformation
+            aiVector3D bonePos, boneScale;
+            aiQuaternion boneRot;
+            boneNode->mTransformation.Decompose(boneScale, boneRot, bonePos);
+
+            bool posEqual = true;
+            bool scaleEqual = true;
+            bool rotEqual = true;
+
+            if (channel->mNumPositionKeys > 0 && !ToVector3(bonePos).Equals(ToVector3(channel->mPositionKeys[0].mValue)))
+                posEqual = false;
+            if (channel->mNumScalingKeys > 0 && !ToVector3(boneScale).Equals(ToVector3(channel->mScalingKeys[0].mValue)))
+                scaleEqual = false;
+            if (channel->mNumRotationKeys > 0 && !ToQuaternion(boneRot).Equals(ToQuaternion(channel->mRotationKeys[0].mValue)))
+                rotEqual = false;
+
             AnimationTrack track;
             AnimationTrack track;
             track.name_ = channelName;
             track.name_ = channelName;
             track.nameHash_ = channelName;
             track.nameHash_ = channelName;
             
             
             // Check which channels are used
             // Check which channels are used
             track.channelMask_ = 0;
             track.channelMask_ = 0;
-            if (channel->mNumPositionKeys > 1)
+            if (channel->mNumPositionKeys > 1 || !posEqual)
                 track.channelMask_ |= CHANNEL_POSITION;
                 track.channelMask_ |= CHANNEL_POSITION;
-            if (channel->mNumRotationKeys > 1)
+            if (channel->mNumRotationKeys > 1 || !rotEqual)
                 track.channelMask_ |= CHANNEL_ROTATION;
                 track.channelMask_ |= CHANNEL_ROTATION;
-            if (channel->mNumScalingKeys > 1)
+            if (channel->mNumScalingKeys > 1 || !scaleEqual)
                 track.channelMask_ |= CHANNEL_SCALE;
                 track.channelMask_ |= CHANNEL_SCALE;
             // Check for redundant identity scale in all keyframes and remove in that case
             // Check for redundant identity scale in all keyframes and remove in that case
             if (track.channelMask_ & CHANNEL_SCALE)
             if (track.channelMask_ & CHANNEL_SCALE)