Browse Source

Adding new Direct3D11/OpenGL3, DetourCrowd, and DetourTileCache files

Josh Engebretson 10 years ago
parent
commit
58dc94b31a
71 changed files with 20394 additions and 0 deletions
  1. 29 0
      Source/Atomic/Graphics/ConstantBuffer.h
  2. 138 0
      Source/Atomic/Graphics/Direct3D11/D3D11ConstantBuffer.cpp
  3. 73 0
      Source/Atomic/Graphics/Direct3D11/D3D11ConstantBuffer.h
  4. 51 0
      Source/Atomic/Graphics/Direct3D11/D3D11GPUObject.cpp
  5. 63 0
      Source/Atomic/Graphics/Direct3D11/D3D11GPUObject.h
  6. 2729 0
      Source/Atomic/Graphics/Direct3D11/D3D11Graphics.cpp
  7. 638 0
      Source/Atomic/Graphics/Direct3D11/D3D11Graphics.h
  8. 65 0
      Source/Atomic/Graphics/Direct3D11/D3D11GraphicsImpl.cpp
  9. 92 0
      Source/Atomic/Graphics/Direct3D11/D3D11GraphicsImpl.h
  10. 391 0
      Source/Atomic/Graphics/Direct3D11/D3D11IndexBuffer.cpp
  11. 107 0
      Source/Atomic/Graphics/Direct3D11/D3D11IndexBuffer.h
  12. 155 0
      Source/Atomic/Graphics/Direct3D11/D3D11RenderSurface.cpp
  13. 101 0
      Source/Atomic/Graphics/Direct3D11/D3D11RenderSurface.h
  14. 88 0
      Source/Atomic/Graphics/Direct3D11/D3D11ShaderProgram.h
  15. 464 0
      Source/Atomic/Graphics/Direct3D11/D3D11ShaderVariation.cpp
  16. 153 0
      Source/Atomic/Graphics/Direct3D11/D3D11ShaderVariation.h
  17. 435 0
      Source/Atomic/Graphics/Direct3D11/D3D11Texture.cpp
  18. 172 0
      Source/Atomic/Graphics/Direct3D11/D3D11Texture.h
  19. 541 0
      Source/Atomic/Graphics/Direct3D11/D3D11Texture2D.cpp
  20. 81 0
      Source/Atomic/Graphics/Direct3D11/D3D11Texture2D.h
  21. 565 0
      Source/Atomic/Graphics/Direct3D11/D3D11Texture3D.cpp
  22. 80 0
      Source/Atomic/Graphics/Direct3D11/D3D11Texture3D.h
  23. 739 0
      Source/Atomic/Graphics/Direct3D11/D3D11TextureCube.cpp
  24. 89 0
      Source/Atomic/Graphics/Direct3D11/D3D11TextureCube.h
  25. 479 0
      Source/Atomic/Graphics/Direct3D11/D3D11VertexBuffer.cpp
  26. 130 0
      Source/Atomic/Graphics/Direct3D11/D3D11VertexBuffer.h
  27. 90 0
      Source/Atomic/Graphics/Direct3D11/D3D11VertexDeclaration.cpp
  28. 53 0
      Source/Atomic/Graphics/Direct3D11/D3D11VertexDeclaration.h
  29. 54 0
      Source/Atomic/Graphics/Direct3D9/D3D9ShaderProgram.h
  30. 139 0
      Source/Atomic/Graphics/OpenGL/OGLConstantBuffer.cpp
  31. 75 0
      Source/Atomic/Graphics/OpenGL/OGLConstantBuffer.h
  32. 509 0
      Source/Atomic/Navigation/CrowdAgent.cpp
  33. 174 0
      Source/Atomic/Navigation/CrowdAgent.h
  34. 486 0
      Source/Atomic/Navigation/DetourCrowdManager.cpp
  35. 138 0
      Source/Atomic/Navigation/DetourCrowdManager.h
  36. 906 0
      Source/Atomic/Navigation/DynamicNavigationMesh.cpp
  37. 106 0
      Source/Atomic/Navigation/DynamicNavigationMesh.h
  38. 86 0
      Source/Atomic/Navigation/NavArea.cpp
  39. 66 0
      Source/Atomic/Navigation/NavArea.h
  40. 96 0
      Source/Atomic/Navigation/NavBuildData.cpp
  41. 119 0
      Source/Atomic/Navigation/NavBuildData.h
  42. 97 0
      Source/Atomic/Navigation/NavigationEvents.h
  43. 117 0
      Source/Atomic/Navigation/Obstacle.cpp
  44. 84 0
      Source/Atomic/Navigation/Obstacle.h
  45. 36 0
      Source/ThirdParty/DetourCrowd/CMakeLists.txt
  46. 18 0
      Source/ThirdParty/DetourCrowd/License.txt
  47. 62 0
      Source/ThirdParty/DetourCrowd/README.md
  48. 120 0
      Source/ThirdParty/DetourCrowd/Readme.txt
  49. 20 0
      Source/ThirdParty/DetourCrowd/TODO.txt
  50. 450 0
      Source/ThirdParty/DetourCrowd/include/DetourCrowd.h
  51. 61 0
      Source/ThirdParty/DetourCrowd/include/DetourLocalBoundary.h
  52. 154 0
      Source/ThirdParty/DetourCrowd/include/DetourObstacleAvoidance.h
  53. 146 0
      Source/ThirdParty/DetourCrowd/include/DetourPathCorridor.h
  54. 75 0
      Source/ThirdParty/DetourCrowd/include/DetourPathQueue.h
  55. 70 0
      Source/ThirdParty/DetourCrowd/include/DetourProximityGrid.h
  56. 1446 0
      Source/ThirdParty/DetourCrowd/source/DetourCrowd.cpp
  57. 137 0
      Source/ThirdParty/DetourCrowd/source/DetourLocalBoundary.cpp
  58. 544 0
      Source/ThirdParty/DetourCrowd/source/DetourObstacleAvoidance.cpp
  59. 597 0
      Source/ThirdParty/DetourCrowd/source/DetourPathCorridor.cpp
  60. 200 0
      Source/ThirdParty/DetourCrowd/source/DetourPathQueue.cpp
  61. 194 0
      Source/ThirdParty/DetourCrowd/source/DetourProximityGrid.cpp
  62. 36 0
      Source/ThirdParty/DetourTileCache/CMakeLists.txt
  63. 18 0
      Source/ThirdParty/DetourTileCache/License.txt
  64. 62 0
      Source/ThirdParty/DetourTileCache/README.md
  65. 120 0
      Source/ThirdParty/DetourTileCache/Readme.txt
  66. 20 0
      Source/ThirdParty/DetourTileCache/TODO.txt
  67. 212 0
      Source/ThirdParty/DetourTileCache/include/DetourTileCache.h
  68. 152 0
      Source/ThirdParty/DetourTileCache/include/DetourTileCacheBuilder.h
  69. 704 0
      Source/ThirdParty/DetourTileCache/source/DetourTileCache.cpp
  70. 2151 0
      Source/ThirdParty/DetourTileCache/source/DetourTileCacheBuilder.cpp
  71. 546 0
      Source/ThirdParty/STB/stb_rect_pack.h

+ 29 - 0
Source/Atomic/Graphics/ConstantBuffer.h

@@ -0,0 +1,29 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#if defined(URHO3D_OPENGL)
+#include "OpenGL/OGLConstantBuffer.h"
+#elif defined(URHO3D_D3D11)
+#include "Direct3D11/D3D11ConstantBuffer.h"
+#endif

+ 138 - 0
Source/Atomic/Graphics/Direct3D11/D3D11ConstantBuffer.cpp

@@ -0,0 +1,138 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/ConstantBuffer.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+
+ConstantBuffer::ConstantBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>())
+{
+}
+
+ConstantBuffer::~ConstantBuffer()
+{
+    Release();
+}
+
+void ConstantBuffer::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+
+        ((ID3D11Buffer*)object_)->Release();
+        object_ = 0;
+    }
+
+    shadowData_.Reset();
+    size_ = 0;
+}
+
+bool ConstantBuffer::SetSize(unsigned size)
+{
+    Release();
+
+    if (!size)
+    {
+        LOGERROR("Can not create zero-sized constant buffer");
+        return false;
+    }
+
+    // Round up to next 16 bytes
+    size += 15;
+    size &= 0xfffffff0;
+
+    size_ = size;
+    dirty_ = false;
+    shadowData_ = new unsigned char[size_];
+    memset(shadowData_.Get(), 0, size_);
+
+    if (graphics_)
+    {
+        D3D11_BUFFER_DESC bufferDesc;
+        memset(&bufferDesc, 0, sizeof bufferDesc);
+
+        bufferDesc.ByteWidth = size_;
+        bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+        bufferDesc.CPUAccessFlags = 0;
+        bufferDesc.Usage = D3D11_USAGE_DEFAULT;
+
+        graphics_->GetImpl()->GetDevice()->CreateBuffer(&bufferDesc, 0, (ID3D11Buffer**)&object_);
+
+        if (!object_)
+        {
+            LOGERROR("Failed to create constant buffer");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void ConstantBuffer::SetParameter(unsigned offset, unsigned size, const void* data)
+{
+    if (offset + size > size_)
+        return; // Would overflow the buffer
+
+    memcpy(&shadowData_[offset], data, size);
+    dirty_ = true;
+}
+
+void ConstantBuffer::SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data)
+{
+    if (offset + rows * 4 * sizeof(float) > size_)
+        return; // Would overflow the buffer
+
+    float* dest = (float*)&shadowData_[offset];
+    const float* src = (const float*)data;
+
+    while (rows--)
+    {
+        *dest++ = *src++;
+        *dest++ = *src++;
+        *dest++ = *src++;
+        ++dest; // Skip over the w coordinate
+    }
+
+    dirty_ = true;
+}
+
+void ConstantBuffer::Apply()
+{
+    if (dirty_ && object_)
+    {
+        graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, 0, shadowData_.Get(), 0, 0);
+        dirty_ = false;
+    }
+}
+
+}

+ 73 - 0
Source/Atomic/Graphics/Direct3D11/D3D11ConstantBuffer.h

@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+#include "../../Core/Object.h"
+
+namespace Urho3D
+{
+
+/// Hardware constant buffer.
+class URHO3D_API ConstantBuffer : public Object, public GPUObject
+{
+    OBJECT(ConstantBuffer);
+    
+public:
+    /// Construct.
+    ConstantBuffer(Context* context);
+    /// Destruct.
+    virtual ~ConstantBuffer();
+    
+    /// Release buffer.
+    virtual void Release();
+    
+    /// Set size and create GPU-side buffer. Return true on success.
+    bool SetSize(unsigned size);
+    /// Set a generic parameter and mark buffer dirty.
+    void SetParameter(unsigned offset, unsigned size, const void* data);
+    /// Set a Vector3 array parameter and mark buffer dirty.
+    void SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data);
+    /// Apply to GPU.
+    void Apply();
+
+    /// Return size.
+    unsigned GetSize() const { return size_; }
+    /// Return whether has unapplied data.
+    bool IsDirty() const { return dirty_; }
+
+private:
+    /// Create buffer.
+    bool Create();
+
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Buffer byte size.
+    unsigned size_;
+    /// Dirty flag.
+    bool dirty_;
+};
+
+}

+ 51 - 0
Source/Atomic/Graphics/Direct3D11/D3D11GPUObject.cpp

@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/GPUObject.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+GPUObject::GPUObject(Graphics* graphics) :
+    graphics_(graphics),
+    object_(0)
+{
+    if (graphics_)
+        graphics->AddGPUObject(this);
+}
+
+GPUObject::~GPUObject()
+{
+    if (graphics_)
+        graphics_->RemoveGPUObject(this);
+}
+
+Graphics* GPUObject::GetGraphics() const
+{
+    return graphics_;
+}
+
+}

+ 63 - 0
Source/Atomic/Graphics/Direct3D11/D3D11GPUObject.h

@@ -0,0 +1,63 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/Ptr.h"
+
+namespace Urho3D
+{
+
+class Graphics;
+
+/// Base class for GPU resources.
+class URHO3D_API GPUObject
+{
+public:
+    /// Construct with graphics subsystem pointer.
+    GPUObject(Graphics* graphics);
+    /// Destruct. Remove from the graphics subsystem.
+    virtual ~GPUObject();
+    
+    /// Unconditionally release the GPU resource.
+    virtual void Release() {}
+    
+    /// Clear the data lost flag. No-op on D3D11.
+    void ClearDataLost() {}
+    
+    /// Return the graphics subsystem.
+    Graphics* GetGraphics() const;
+    /// Return Direct3D object.
+    void* GetGPUObject() const { return object_; }
+    /// Return whether data is lost due to device loss. Always false on D3D11.
+    bool IsDataLost() const { return false; }
+    /// Return whether has pending data assigned while device was lost. Always false on D3D11.
+    bool HasPendingData() const { return false; }
+    
+protected:
+    /// Graphics subsystem.
+    WeakPtr<Graphics> graphics_;
+    /// Direct3D object.
+    void* object_;
+};
+
+}

+ 2729 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Graphics.cpp

@@ -0,0 +1,2729 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/AnimatedModel.h"
+#include "../../Graphics/Animation.h"
+#include "../../Graphics/AnimationController.h"
+#include "../../Graphics/Camera.h"
+#include "../../Graphics/ConstantBuffer.h"
+#include "../../Core/Context.h"
+#include "../../Graphics/CustomGeometry.h"
+#include "../../Graphics/DebugRenderer.h"
+#include "../../Graphics/DecalSet.h"
+#include "../../IO/File.h"
+#include "../../Graphics/Geometry.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/IndexBuffer.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Material.h"
+#include "../../Graphics/Octree.h"
+#include "../../Graphics/ParticleEffect.h"
+#include "../../Graphics/ParticleEmitter.h"
+#include "../../Core/ProcessUtils.h"
+#include "../../Core/Profiler.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/Shader.h"
+#include "../../Graphics/ShaderPrecache.h"
+#include "../../Graphics/ShaderProgram.h"
+#include "../../Graphics/ShaderVariation.h"
+#include "../../Graphics/Skybox.h"
+#include "../../Graphics/StaticModelGroup.h"
+#include "../../Graphics/Technique.h"
+#include "../../Graphics/Terrain.h"
+#include "../../Graphics/TerrainPatch.h"
+#include "../../Graphics/Texture2D.h"
+#include "../../Graphics/Texture3D.h"
+#include "../../Graphics/TextureCube.h"
+#include "../../Core/Timer.h"
+#include "../../Graphics/VertexBuffer.h"
+#include "../../Graphics/VertexDeclaration.h"
+#include "../../Graphics/Zone.h"
+
+#include <SDL/SDL_syswm.h>
+
+#include "../../DebugNew.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355)
+#endif
+
+// On Intel / NVIDIA setups prefer the NVIDIA GPU
+extern "C" {
+    __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+}
+
+namespace Urho3D
+{
+
+static const D3D11_COMPARISON_FUNC d3dCmpFunc[] =
+{
+    D3D11_COMPARISON_ALWAYS,
+    D3D11_COMPARISON_EQUAL,
+    D3D11_COMPARISON_NOT_EQUAL,
+    D3D11_COMPARISON_LESS,
+    D3D11_COMPARISON_LESS_EQUAL,
+    D3D11_COMPARISON_GREATER,
+    D3D11_COMPARISON_GREATER_EQUAL
+};
+
+static const DWORD d3dBlendEnable[] =
+{
+    FALSE,
+    TRUE,
+    TRUE,
+    TRUE,
+    TRUE,
+    TRUE,
+    TRUE,
+    TRUE
+};
+
+static const D3D11_BLEND d3dSrcBlend[] =
+{
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_DEST_COLOR,
+    D3D11_BLEND_SRC_ALPHA,
+    D3D11_BLEND_SRC_ALPHA,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_INV_DEST_ALPHA,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_SRC_ALPHA,
+};
+
+static const D3D11_BLEND d3dDestBlend[] =
+{
+    D3D11_BLEND_ZERO,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_ZERO,
+    D3D11_BLEND_INV_SRC_ALPHA,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_INV_SRC_ALPHA,
+    D3D11_BLEND_DEST_ALPHA,
+    D3D11_BLEND_ONE,
+    D3D11_BLEND_ONE
+};
+
+static const D3D11_BLEND_OP d3dBlendOp[] =
+{
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_ADD,
+    D3D11_BLEND_OP_REV_SUBTRACT,
+    D3D11_BLEND_OP_REV_SUBTRACT
+};
+
+static const D3D11_STENCIL_OP d3dStencilOp[] =
+{
+    D3D11_STENCIL_OP_KEEP,
+    D3D11_STENCIL_OP_ZERO,
+    D3D11_STENCIL_OP_REPLACE,
+    D3D11_STENCIL_OP_INCR,
+    D3D11_STENCIL_OP_DECR
+};
+
+static const D3D11_CULL_MODE d3dCullMode[] =
+{
+    D3D11_CULL_NONE,
+    D3D11_CULL_BACK,
+    D3D11_CULL_FRONT
+};
+
+static const D3D11_FILL_MODE d3dFillMode[] =
+{
+    D3D11_FILL_SOLID,
+    D3D11_FILL_WIREFRAME,
+    D3D11_FILL_WIREFRAME // Point mode not supported
+};
+
+static unsigned GetD3DColor(const Color& color)
+{
+    unsigned r = (unsigned)(Clamp(color.r_ * 255.0f, 0.0f, 255.0f));
+    unsigned g = (unsigned)(Clamp(color.g_ * 255.0f, 0.0f, 255.0f));
+    unsigned b = (unsigned)(Clamp(color.b_ * 255.0f, 0.0f, 255.0f));
+    unsigned a = (unsigned)(Clamp(color.a_ * 255.0f, 0.0f, 255.0f));
+    return (((a) & 0xff) << 24) | (((r) & 0xff) << 16) | (((g) & 0xff) << 8) | ((b) & 0xff);
+}
+
+static void GetD3DPrimitiveType(unsigned elementCount, PrimitiveType type, unsigned& primitiveCount, D3D_PRIMITIVE_TOPOLOGY& d3dPrimitiveType)
+{
+    switch (type)
+    {
+    case TRIANGLE_LIST:
+        primitiveCount = elementCount / 3;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+        break;
+        
+    case LINE_LIST:
+        primitiveCount = elementCount / 2;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
+        break;
+
+    case POINT_LIST:
+        primitiveCount = elementCount;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
+        break;
+        
+    case TRIANGLE_STRIP:
+        primitiveCount = elementCount - 2;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+        break;
+        
+    case LINE_STRIP:
+        primitiveCount = elementCount - 1;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
+        break;
+        
+    case TRIANGLE_FAN:
+        // Triangle fan is not supported on D3D11
+        primitiveCount = 0;
+        d3dPrimitiveType = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
+        break;
+    }
+}
+
+static HWND GetWindowHandle(SDL_Window* window)
+{
+    SDL_SysWMinfo sysInfo;
+    
+    SDL_VERSION(&sysInfo.version);
+    SDL_GetWindowWMInfo(window, &sysInfo);
+    return sysInfo.info.win.window;
+}
+
+static unsigned readableDepthFormat = 0;
+
+const Vector2 Graphics::pixelUVOffset(0.0f, 0.0f);
+
+Graphics::Graphics(Context* context) :
+    Object(context),
+    impl_(new GraphicsImpl()),
+    windowIcon_(0),
+    externalWindow_(0),
+    width_(0),
+    height_(0),
+    position_(SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED),
+    multiSample_(1),
+    fullscreen_(false),
+    borderless_(false),
+    resizable_(false),
+    vsync_(false),
+    tripleBuffer_(false),
+    flushGPU_(false),
+    sRGB_(false),
+    lightPrepassSupport_(false),
+    deferredSupport_(false),
+    instancingSupport_(false),
+    sRGBSupport_(false),
+    sRGBWriteSupport_(false),
+    numPrimitives_(0),
+    numBatches_(0),
+    maxScratchBufferRequest_(0),
+    defaultTextureFilterMode_(FILTER_TRILINEAR),
+    shaderProgram_(0),
+    shaderPath_("Shaders/HLSL/"),
+    shaderExtension_(".hlsl"),
+    orientations_("LandscapeLeft LandscapeRight"),
+    apiName_("D3D11")
+{
+    SetTextureUnitMappings();
+    ResetCachedState();
+    
+    // Initialize SDL now. Graphics should be the first SDL-using subsystem to be created
+    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_NOPARACHUTE);
+    
+    // Register Graphics library object factories
+    RegisterGraphicsLibrary(context_);
+}
+
+Graphics::~Graphics()
+{
+    {
+        MutexLock lock(gpuObjectMutex_);
+
+        // Release all GPU objects that still exist
+        for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+            (*i)->Release();
+        gpuObjects_.Clear();
+    }
+
+    vertexDeclarations_.Clear();
+    constantBuffers_.Clear();
+    
+    for (HashMap<unsigned, ID3D11BlendState*>::Iterator i = impl_->blendStates_.Begin(); i != impl_->blendStates_.End(); ++i)
+    {
+        if (i->second_)
+            i->second_->Release();
+    }
+    impl_->blendStates_.Clear();
+    
+    for (HashMap<unsigned, ID3D11DepthStencilState*>::Iterator i = impl_->depthStates_.Begin(); i != impl_->depthStates_.End(); ++i)
+    {
+        if (i->second_)
+            i->second_->Release();
+    }
+    impl_->depthStates_.Clear();
+
+    for (HashMap<unsigned, ID3D11RasterizerState*>::Iterator i = impl_->rasterizerStates_.Begin(); i != impl_->rasterizerStates_.End(); ++i)
+    {
+        if (i->second_)
+            i->second_->Release();
+    }
+    impl_->rasterizerStates_.Clear();
+
+    if (impl_->defaultRenderTargetView_)
+    {
+        impl_->defaultRenderTargetView_->Release();
+        impl_->defaultRenderTargetView_ = 0;
+    }
+    if (impl_->defaultDepthStencilView_)
+    {
+        impl_->defaultDepthStencilView_->Release();
+        impl_->defaultDepthStencilView_ = 0;
+    }
+    if (impl_->defaultDepthTexture_)
+    {
+        impl_->defaultDepthTexture_->Release();
+        impl_->defaultDepthTexture_ = 0;
+    }
+    if (impl_->swapChain_)
+    {
+        impl_->swapChain_->Release();
+        impl_->swapChain_ = 0;
+    }
+    if (impl_->deviceContext_)
+    {
+        impl_->deviceContext_->Release();
+        impl_->deviceContext_ = 0;
+    }
+    if (impl_->device_)
+    {
+        impl_->device_->Release();
+        impl_->device_ = 0;
+    }
+    if (impl_->window_)
+    {
+        SDL_ShowCursor(SDL_TRUE);
+        SDL_DestroyWindow(impl_->window_);
+        impl_->window_ = 0;
+    }
+    
+    delete impl_;
+    impl_ = 0;
+    
+    // Shut down SDL now. Graphics should be the last SDL-using subsystem to be destroyed
+    SDL_Quit();
+}
+
+void Graphics::SetExternalWindow(void* window)
+{
+    if (!impl_->window_)
+        externalWindow_ = window;
+    else
+        LOGERROR("Window already opened, can not set external window");
+}
+
+void Graphics::SetWindowTitle(const String& windowTitle)
+{
+    windowTitle_ = windowTitle;
+    if (impl_->window_)
+        SDL_SetWindowTitle(impl_->window_, windowTitle_.CString());
+}
+
+void Graphics::SetWindowIcon(Image* windowIcon)
+{
+    windowIcon_ = windowIcon;
+    if (impl_->window_)
+        CreateWindowIcon();
+}
+
+void Graphics::SetWindowPosition(const IntVector2& position)
+{
+    if (impl_->window_)
+        SDL_SetWindowPosition(impl_->window_, position.x_, position.y_);
+    else
+        position_ = position; // Sets as initial position for OpenWindow()
+}
+
+void Graphics::SetWindowPosition(int x, int y)
+{
+    SetWindowPosition(IntVector2(x, y));
+}
+
+bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable, bool vsync, bool tripleBuffer, int multiSample)
+{
+    PROFILE(SetScreenMode);
+
+    bool maximize = false;
+    
+    // Find out the full screen mode display format (match desktop color depth)
+    SDL_DisplayMode mode;
+    SDL_GetDesktopDisplayMode(0, &mode);
+    DXGI_FORMAT fullscreenFormat = SDL_BITSPERPIXEL(mode.format) == 16 ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM;
+    
+    // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size. If zero in fullscreen, use desktop mode
+    if (!width || !height)
+    {
+        if (fullscreen || borderless)
+        {
+            width = mode.w;
+            height = mode.h;
+        }
+        else
+        {
+            maximize = resizable;
+            width = 1024;
+            height = 768;
+        }
+    }
+    
+    // Fullscreen or Borderless can not be resizable
+    if (fullscreen || borderless)
+        resizable = false;
+
+    // Borderless cannot be fullscreen, they are mutually exclusive
+    if (borderless)
+        fullscreen = false;
+    
+    // If nothing changes, do not reset the device
+    if (width == width_ && height == height_ && fullscreen == fullscreen_ && borderless == borderless_ && resizable == resizable_ &&
+        vsync == vsync_ && tripleBuffer == tripleBuffer_ && multiSample == multiSample_)
+        return true;
+    
+    SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
+
+    if (!impl_->window_)
+    {
+        if (!OpenWindow(width, height, resizable, borderless))
+            return false;
+    }
+    
+    // Check fullscreen mode validity. Use a closest match if not found
+    if (fullscreen)
+    {
+        PODVector<IntVector2> resolutions = GetResolutions();
+        if (resolutions.Empty())
+            fullscreen = false;
+        else
+        {
+            unsigned best = 0;
+            unsigned bestError = M_MAX_UNSIGNED;
+
+            for (unsigned i = 0; i < resolutions.Size(); ++i)
+            {
+                unsigned error = Abs(resolutions[i].x_ - width) + Abs(resolutions[i].y_ - height);
+                if (error < bestError)
+                {
+                    best = i;
+                    bestError = error;
+                }
+            }
+            
+            width = resolutions[best].x_;
+            height = resolutions[best].y_;
+        }
+    }
+    
+    AdjustWindow(width, height, fullscreen, borderless);
+
+    if (maximize)
+    {
+        Maximize();
+        SDL_GetWindowSize(impl_->window_, &width, &height);
+    }
+    
+    if (!impl_->device_ || multiSample_ != multiSample)
+        CreateDevice(width, height, multiSample);
+    UpdateSwapChain(width, height);
+
+    fullscreen_ = fullscreen;
+    borderless_ = borderless;
+    resizable_ = resizable;
+    vsync_ = vsync;
+    tripleBuffer_ = tripleBuffer;
+    
+    // Clear the initial window contents to black
+    Clear(CLEAR_COLOR);
+    impl_->swapChain_->Present(0, 0);
+    
+    #ifdef URHO3D_LOGGING
+    String msg;
+    msg.AppendWithFormat("Set screen mode %dx%d %s", width_, height_, (fullscreen_ ? "fullscreen" : "windowed"));
+    if (borderless_)
+        msg.Append(" borderless");
+    if (resizable_)
+        msg.Append(" resizable");
+    if (multiSample > 1)
+        msg.AppendWithFormat(" multisample %d", multiSample);
+    LOGINFO(msg);
+    #endif
+
+    using namespace ScreenMode;
+    
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_WIDTH] = width_;
+    eventData[P_HEIGHT] = height_;
+    eventData[P_FULLSCREEN] = fullscreen_;
+    eventData[P_RESIZABLE] = resizable_;
+    eventData[P_BORDERLESS] = borderless_;
+    SendEvent(E_SCREENMODE, eventData);
+    
+    return true;
+}
+
+bool Graphics::SetMode(int width, int height)
+{
+    return SetMode(width, height, fullscreen_, borderless_, resizable_, vsync_, tripleBuffer_, multiSample_);
+}
+
+void Graphics::SetSRGB(bool enable)
+{
+    bool newEnable = enable && sRGBWriteSupport_;
+    if (newEnable != sRGB_)
+    {
+        sRGB_ = newEnable;
+        if (impl_->swapChain_)
+        {
+            // Recreate swap chain for the new backbuffer format
+            CreateDevice(width_, height_, multiSample_);
+            UpdateSwapChain(width_, height_);
+        }
+    }
+}
+
+void Graphics::SetFlushGPU(bool enable)
+{
+    flushGPU_ = enable;
+    
+    if (impl_->device_)
+    {
+        IDXGIDevice1* dxgiDevice;
+        impl_->device_->QueryInterface(IID_IDXGIDevice1, (void **)&dxgiDevice);
+        if (dxgiDevice)
+        {
+            dxgiDevice->SetMaximumFrameLatency(enable ? 1 : 3);
+            dxgiDevice->Release();
+        }
+    }
+}
+
+void Graphics::SetOrientations(const String& orientations)
+{
+    orientations_ = orientations.Trimmed();
+    SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
+}
+
+bool Graphics::ToggleFullscreen()
+{
+    return SetMode(width_, height_, !fullscreen_, borderless_, resizable_, vsync_, tripleBuffer_, multiSample_);
+}
+
+void Graphics::Close()
+{
+    if (impl_->window_)
+    {
+        SDL_ShowCursor(SDL_TRUE);
+        SDL_DestroyWindow(impl_->window_);
+        impl_->window_ = 0;
+    }
+}
+
+bool Graphics::TakeScreenShot(Image& destImage)
+{
+    PROFILE(TakeScreenShot);
+
+    if (!impl_->device_)
+        return false;
+
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = width_;
+    textureDesc.Height = height_;
+    textureDesc.MipLevels = 1;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = D3D11_USAGE_STAGING;
+    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+
+    ID3D11Texture2D* stagingTexture = 0;
+    impl_->device_->CreateTexture2D(&textureDesc, 0, &stagingTexture);
+    if (!stagingTexture)
+    {
+        LOGERROR("Could not create staging texture for screenshot");
+        return false;
+    }
+
+    ID3D11Resource* source = 0;
+    impl_->defaultRenderTargetView_->GetResource(&source);
+
+    if (multiSample_ > 1)
+    {
+        // If backbuffer is multisampled, need another DEFAULT usage texture to resolve the data to first
+        textureDesc.Usage = D3D11_USAGE_DEFAULT;
+        textureDesc.CPUAccessFlags = 0;
+        ID3D11Texture2D* resolveTexture = 0;
+
+        impl_->device_->CreateTexture2D(&textureDesc, 0, &resolveTexture);
+        if (!resolveTexture)
+        {
+            LOGERROR("Could not create intermediate texture for multisampled screenshot");
+            stagingTexture->Release();
+            return false;
+        }
+
+        impl_->deviceContext_->ResolveSubresource(resolveTexture, 0, source, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
+        impl_->deviceContext_->CopyResource(stagingTexture, resolveTexture);
+        resolveTexture->Release();
+    }
+    else
+        impl_->deviceContext_->CopyResource(stagingTexture, source);
+
+    source->Release();
+
+    D3D11_MAPPED_SUBRESOURCE mappedData;
+    mappedData.pData = 0;
+    impl_->deviceContext_->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedData);
+
+    destImage.SetSize(width_, height_, 3);
+    unsigned char* destData = destImage.GetData();
+    if (mappedData.pData)
+    {
+        for (int y = 0; y < height_; ++y)
+        {
+            unsigned char* src = (unsigned char*)mappedData.pData + y * mappedData.RowPitch;
+            for (int x = 0; x < width_; ++x)
+            {
+                *destData++ = *src++;
+                *destData++ = *src++;
+                *destData++ = *src++;
+                ++src;
+            }
+        }
+        impl_->deviceContext_->Unmap(stagingTexture, 0);
+        stagingTexture->Release();
+        return true;
+    }
+    else
+    {
+        LOGERROR("Could not map staging texture for screenshot");
+        stagingTexture->Release();
+        return false;
+    }
+}
+
+bool Graphics::BeginFrame()
+{
+    if (!IsInitialized())
+        return false;
+    
+    // If using an external window, check it for size changes, and reset screen mode if necessary
+    if (externalWindow_)
+    {
+        int width, height;
+        
+        SDL_GetWindowSize(impl_->window_, &width, &height);
+        if (width != width_ || height != height_)
+            SetMode(width, height);
+    }
+    else
+    {
+        // To prevent a loop of endless device loss and flicker, do not attempt to render when in fullscreen
+        // and the window is minimized
+        if (fullscreen_ && (SDL_GetWindowFlags(impl_->window_) & SDL_WINDOW_MINIMIZED))
+            return false;
+    }
+    
+    // Set default rendertarget and depth buffer
+    ResetRenderTargets();
+    
+    // Cleanup textures from previous frame
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        SetTexture(i, 0);
+    
+    numPrimitives_ = 0;
+    numBatches_ = 0;
+    
+    SendEvent(E_BEGINRENDERING);
+    
+    return true;
+}
+
+void Graphics::EndFrame()
+{
+    if (!IsInitialized())
+        return;
+    
+    {
+        PROFILE(Present);
+        
+        SendEvent(E_ENDRENDERING);
+        
+        impl_->swapChain_->Present(vsync_ ? 1 : 0, 0);
+    }
+    
+    // Clean up too large scratch buffers
+    CleanupScratchBuffers();
+}
+
+void Graphics::Clear(unsigned flags, const Color& color, float depth, unsigned stencil)
+{
+    PrepareDraw();
+
+    IntVector2 rtSize = GetRenderTargetDimensions();
+    
+    // D3D11 clear always clears the whole target regardless of viewport or scissor test settings
+    // Emulate partial clear by rendering a quad
+    if (!viewport_.left_ && !viewport_.top_ && viewport_.right_ == rtSize.x_ && viewport_.bottom_ == rtSize.y_)
+    {
+        if ((flags & CLEAR_COLOR) && impl_->renderTargetViews_[0])
+            impl_->deviceContext_->ClearRenderTargetView(impl_->renderTargetViews_[0], color.Data());
+        
+        if ((flags & (CLEAR_DEPTH | CLEAR_STENCIL)) && impl_->depthStencilView_)
+        {
+            unsigned depthClearFlags = 0;
+            if (flags & CLEAR_DEPTH)
+                depthClearFlags |= D3D11_CLEAR_DEPTH;
+            if (flags & CLEAR_STENCIL)
+                depthClearFlags |= D3D11_CLEAR_STENCIL;
+            impl_->deviceContext_->ClearDepthStencilView(impl_->depthStencilView_, depthClearFlags, depth, stencil);
+        }
+    }
+    else
+    {
+        Renderer* renderer = GetSubsystem<Renderer>();
+        if (!renderer)
+            return;
+
+        Geometry* geometry = renderer->GetQuadGeometry();
+
+        Matrix3x4 model = Matrix3x4::IDENTITY;
+        Matrix4 projection = Matrix4::IDENTITY;
+        model.m23_ = Clamp(depth, 0.0f, 1.0f);
+
+        SetColorWrite((flags & CLEAR_COLOR) != 0);
+        SetCullMode(CULL_NONE);
+        SetDepthTest(CMP_ALWAYS);
+        SetDepthWrite((flags & CLEAR_DEPTH) != 0);
+        SetFillMode(FILL_SOLID);
+        SetScissorTest(false);
+        SetStencilTest((flags & CLEAR_STENCIL) != 0, CMP_ALWAYS, OP_REF, OP_KEEP, OP_KEEP, stencil);
+        SetShaders(GetShader(VS, "ClearFramebuffer"), GetShader(PS, "ClearFramebuffer"));
+        SetShaderParameter(VSP_MODEL, model);
+        SetShaderParameter(VSP_VIEWPROJ, projection);
+        SetShaderParameter(PSP_MATDIFFCOLOR, color);
+
+        geometry->Draw(this);
+        
+        SetColorWrite(true);
+        SetDepthWrite(true);
+        SetStencilTest(false);
+        ClearParameterSources();
+    }
+}
+
+bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
+{
+    if (!destination || !destination->GetRenderSurface())
+        return false;
+    
+    PROFILE(ResolveToTexture);
+    
+    IntRect vpCopy = viewport;
+    if (vpCopy.right_ <= vpCopy.left_)
+        vpCopy.right_ = vpCopy.left_ + 1;
+    if (vpCopy.bottom_ <= vpCopy.top_)
+        vpCopy.bottom_ = vpCopy.top_ + 1;
+    
+    RECT rect;
+    rect.left = Clamp(vpCopy.left_, 0, width_);
+    rect.top = Clamp(vpCopy.top_, 0, height_);
+    rect.right = Clamp(vpCopy.right_, 0, width_);
+    rect.bottom = Clamp(vpCopy.bottom_, 0, height_);
+    
+    RECT destRect;
+    destRect.left = 0;
+    destRect.top = 0;
+    destRect.right = destination->GetWidth();
+    destRect.bottom = destination->GetHeight();
+    
+    ID3D11Resource* source = 0;
+    bool resolve = false;
+    bool needRelease = false;
+
+    if (renderTargets_[0])
+        source = (ID3D11Resource*)renderTargets_[0]->GetParentTexture()->GetGPUObject();
+    else
+    {
+        impl_->defaultRenderTargetView_->GetResource(&source);
+        resolve = multiSample_ > 1;
+        needRelease = true;
+    }
+
+    if (!resolve)
+        impl_->deviceContext_->CopyResource((ID3D11Resource*)destination->GetGPUObject(), source);
+    else
+    {
+        impl_->deviceContext_->ResolveSubresource((ID3D11Resource*)destination->GetGPUObject(), 0, source, 0, (DXGI_FORMAT)
+            destination->GetFormat());
+    }
+
+    if (needRelease)
+        source->Release();
+
+    return true;
+}
+
+void Graphics::Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount)
+{
+    if (!vertexCount || !shaderProgram_)
+        return;
+    
+    PrepareDraw();
+
+    unsigned primitiveCount;
+    D3D_PRIMITIVE_TOPOLOGY d3dPrimitiveType;
+    
+    GetD3DPrimitiveType(vertexCount, type, primitiveCount, d3dPrimitiveType);
+    if (d3dPrimitiveType != primitiveType_)
+    {
+        impl_->deviceContext_->IASetPrimitiveTopology(d3dPrimitiveType);
+        primitiveType_ = d3dPrimitiveType;
+    }
+    impl_->deviceContext_->Draw(vertexCount, vertexStart);
+
+    numPrimitives_ += primitiveCount;
+    ++numBatches_;
+}
+
+void Graphics::Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount)
+{
+    if (!vertexCount || !shaderProgram_)
+        return;
+    
+    PrepareDraw();
+
+    unsigned primitiveCount;
+    D3D_PRIMITIVE_TOPOLOGY d3dPrimitiveType;
+    
+    GetD3DPrimitiveType(indexCount, type, primitiveCount, d3dPrimitiveType);
+    if (d3dPrimitiveType != primitiveType_)
+    {
+        impl_->deviceContext_->IASetPrimitiveTopology(d3dPrimitiveType);
+        primitiveType_ = d3dPrimitiveType;
+    }
+    impl_->deviceContext_->DrawIndexed(indexCount, indexStart, 0);
+
+    numPrimitives_ += primitiveCount;
+    ++numBatches_;
+}
+
+void Graphics::DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount,
+    unsigned instanceCount)
+{
+    if (!indexCount || !instanceCount || !shaderProgram_)
+        return;
+    
+    PrepareDraw();
+
+    unsigned primitiveCount;
+    D3D_PRIMITIVE_TOPOLOGY d3dPrimitiveType;
+    
+    GetD3DPrimitiveType(indexCount, type, primitiveCount, d3dPrimitiveType);
+    if (d3dPrimitiveType != primitiveType_)
+    {
+        impl_->deviceContext_->IASetPrimitiveTopology(d3dPrimitiveType);
+        primitiveType_ = d3dPrimitiveType;
+    }
+    impl_->deviceContext_->DrawIndexedInstanced(indexCount, instanceCount, indexStart, 0, 0);
+
+    numPrimitives_ += instanceCount * primitiveCount;
+    ++numBatches_;
+}
+
+void Graphics::SetVertexBuffer(VertexBuffer* buffer)
+{
+    // Note: this is not multi-instance safe
+    static PODVector<VertexBuffer*> vertexBuffers(1);
+    static PODVector<unsigned> elementMasks(1);
+    vertexBuffers[0] = buffer;
+    elementMasks[0] = MASK_DEFAULT;
+    SetVertexBuffers(vertexBuffers, elementMasks);
+}
+
+bool Graphics::SetVertexBuffers(const PODVector<VertexBuffer*>& buffers, const PODVector<unsigned>&
+    elementMasks, unsigned instanceOffset)
+{
+    if (buffers.Size() > MAX_VERTEX_STREAMS)
+    {
+        LOGERROR("Too many vertex buffers");
+        return false;
+    }
+    if (buffers.Size() != elementMasks.Size())
+    {
+        LOGERROR("Amount of element masks and vertex buffers does not match");
+        return false;
+    }
+    
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        VertexBuffer* buffer = 0;
+        bool changed = false;
+
+        buffer = i < buffers.Size() ? buffers[i] : 0;
+        if (buffer)
+        {
+            unsigned elementMask = buffer->GetElementMask() & elementMasks[i];
+            unsigned offset = (elementMask & MASK_INSTANCEMATRIX1) ? instanceOffset * buffer->GetVertexSize() : 0;
+
+            if (buffer != vertexBuffers_[i] || elementMask != elementMasks_[i] || offset != impl_->vertexOffsets_[i])
+            {
+                vertexBuffers_[i] = buffer;
+                elementMasks_[i] = elementMask;
+                impl_->vertexBuffers_[i] = (ID3D11Buffer*)buffer->GetGPUObject();
+                impl_->vertexSizes_[i] = buffer->GetVertexSize();
+                impl_->vertexOffsets_[i] = offset;
+                changed = true;
+            }
+        }
+        else if (vertexBuffers_[i])
+        {
+            vertexBuffers_[i] = 0;
+            elementMasks_[i] = 0;
+            impl_->vertexBuffers_[i] = 0;
+            impl_->vertexSizes_[i] = 0;
+            impl_->vertexOffsets_[i] = 0;
+            changed = true;
+        }
+
+        if (changed)
+        {
+            vertexDeclarationDirty_ = true;
+
+            if (firstDirtyVB_ == M_MAX_UNSIGNED)
+                firstDirtyVB_ = lastDirtyVB_ = i;
+            else
+            {
+                if (i < firstDirtyVB_)
+                    firstDirtyVB_ = i;
+                if (i > lastDirtyVB_)
+                    lastDirtyVB_ = i;
+            }
+        }
+    }
+    
+    return true;
+}
+
+bool Graphics::SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>&
+    elementMasks, unsigned instanceOffset)
+{
+    return SetVertexBuffers(reinterpret_cast<const PODVector<VertexBuffer*>&>(buffers), elementMasks, instanceOffset);
+}
+
+void Graphics::SetIndexBuffer(IndexBuffer* buffer)
+{
+    if (buffer != indexBuffer_)
+    {
+        if (buffer)
+        {
+            impl_->deviceContext_->IASetIndexBuffer((ID3D11Buffer*)buffer->GetGPUObject(), buffer->GetIndexSize() ==
+                sizeof(unsigned short) ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
+        }
+        else
+            impl_->deviceContext_->IASetIndexBuffer(0, DXGI_FORMAT_UNKNOWN, 0);
+
+        indexBuffer_ = buffer;
+    }
+}
+
+void Graphics::SetShaders(ShaderVariation* vs, ShaderVariation* ps)
+{
+    // Switch to the clip plane variations if necessary
+    /// \todo Causes overhead and string manipulation per drawcall
+    if (useClipPlane_)
+    {
+        if (vs)
+            vs = vs->GetOwner()->GetVariation(VS, vs->GetDefines() + " CLIPPLANE");
+        if (ps)
+            ps = ps->GetOwner()->GetVariation(PS, ps->GetDefines() + " CLIPPLANE");
+    }
+
+    if (vs == vertexShader_ && ps == pixelShader_)
+        return;
+    
+    if (vs != vertexShader_)
+    {
+        // Create the shader now if not yet created. If already attempted, do not retry
+        if (vs && !vs->GetGPUObject())
+        {
+            if (vs->GetCompilerOutput().Empty())
+            {
+                PROFILE(CompileVertexShader);
+
+                bool success = vs->Create();
+                if (!success)
+                {
+                    LOGERROR("Failed to compile vertex shader " + vs->GetFullName() + ":\n" + vs->GetCompilerOutput());
+                    vs = 0;
+                }
+            }
+            else
+                vs = 0;
+        }
+        
+        impl_->deviceContext_->VSSetShader((ID3D11VertexShader*)(vs ? vs->GetGPUObject() : 0), 0, 0);
+        vertexShader_ = vs;
+        vertexDeclarationDirty_ = true;
+    }
+    
+    if (ps != pixelShader_)
+    {
+        if (ps && !ps->GetGPUObject())
+        {
+            if (ps->GetCompilerOutput().Empty())
+            {
+                PROFILE(CompilePixelShader);
+
+                bool success = ps->Create();
+                if (!success)
+                {
+                    LOGERROR("Failed to compile pixel shader " + ps->GetFullName() + ":\n" + ps->GetCompilerOutput());
+                    ps = 0;
+                }
+            }
+            else
+                ps = 0;
+        }
+        
+        impl_->deviceContext_->PSSetShader((ID3D11PixelShader*)(ps ? ps->GetGPUObject() : 0), 0, 0);
+        pixelShader_ = ps;
+    }
+    
+    // Update current shader parameters & constant buffers
+    if (vertexShader_ && pixelShader_)
+    {
+        Pair<ShaderVariation*, ShaderVariation*> key = MakePair(vertexShader_, pixelShader_);
+        ShaderProgramMap::Iterator i = shaderPrograms_.Find(key);
+        if (i != shaderPrograms_.End())
+            shaderProgram_ = i->second_.Get();
+        else
+        {
+            ShaderProgram* newProgram = shaderPrograms_[key] = new ShaderProgram(this, vertexShader_, pixelShader_);
+            shaderProgram_ = newProgram;
+        }
+
+        bool vsBuffersChanged = false;
+        bool psBuffersChanged = false;
+
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        {
+            ID3D11Buffer* vsBuffer = shaderProgram_->vsConstantBuffers_[i] ? (ID3D11Buffer*)shaderProgram_->vsConstantBuffers_[i]->
+                GetGPUObject() : 0;
+            if (vsBuffer != impl_->constantBuffers_[VS][i])
+            {
+                impl_->constantBuffers_[VS][i] = vsBuffer;
+                shaderParameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+                vsBuffersChanged = true;
+            }
+
+            ID3D11Buffer* psBuffer = shaderProgram_->psConstantBuffers_[i] ? (ID3D11Buffer*)shaderProgram_->psConstantBuffers_[i]->
+                GetGPUObject() : 0;
+            if (psBuffer != impl_->constantBuffers_[PS][i])
+            {
+                impl_->constantBuffers_[PS][i] = psBuffer;
+                shaderParameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+                psBuffersChanged = true;
+            }
+        }
+
+        if (vsBuffersChanged)
+            impl_->deviceContext_->VSSetConstantBuffers(0, MAX_SHADER_PARAMETER_GROUPS, &impl_->constantBuffers_[VS][0]);
+        if (psBuffersChanged)
+            impl_->deviceContext_->PSSetConstantBuffers(0, MAX_SHADER_PARAMETER_GROUPS, &impl_->constantBuffers_[PS][0]);
+    }
+    else
+        shaderProgram_ = 0;
+
+    // Store shader combination if shader dumping in progress
+    if (shaderPrecache_)
+        shaderPrecache_->StoreShaders(vertexShader_, pixelShader_);
+
+    // Update clip plane parameter if necessary
+    if (useClipPlane_)
+        SetShaderParameter(VSP_CLIPPLANE, clipPlane_);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const float* data, unsigned count)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, count * sizeof(float), data);
+}
+
+void Graphics::SetShaderParameter(StringHash param, float value)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(float), &value);
+}
+
+void Graphics::SetShaderParameter(StringHash param, bool value)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(bool), &value);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Color& color)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Color), &color);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Vector2& vector)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Vector2), &vector);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix3& matrix)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetVector3ArrayParameter(i->second_.offset_, 3, &matrix);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Vector3& vector)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Vector3), &vector);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix4& matrix)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Matrix4), &matrix);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Vector4& vector)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Vector4), &vector);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Matrix3x4& matrix)
+{
+    HashMap<StringHash, ShaderParameter>::Iterator i;
+    if (!shaderProgram_ || (i = shaderProgram_->parameters_.Find(param)) == shaderProgram_->parameters_.End())
+        return;
+
+    ConstantBuffer* buffer = i->second_.bufferPtr_;
+    if (!buffer->IsDirty())
+        dirtyConstantBuffers_.Push(buffer);
+    buffer->SetParameter(i->second_.offset_, sizeof(Matrix3x4), &matrix);
+}
+
+void Graphics::SetShaderParameter(StringHash param, const Variant& value)
+{
+    switch (value.GetType())
+    {
+    case VAR_BOOL:
+        SetShaderParameter(param, value.GetBool());
+        break;
+
+    case VAR_FLOAT:
+        SetShaderParameter(param, value.GetFloat());
+        break;
+        
+    case VAR_VECTOR2:
+        SetShaderParameter(param, value.GetVector2());
+        break;
+        
+    case VAR_VECTOR3:
+        SetShaderParameter(param, value.GetVector3());
+        break;
+        
+    case VAR_VECTOR4:
+        SetShaderParameter(param, value.GetVector4());
+        break;
+        
+    case VAR_COLOR:
+        SetShaderParameter(param, value.GetColor());
+        break;
+
+    case VAR_MATRIX3:
+        SetShaderParameter(param, value.GetMatrix3());
+        break;
+        
+    case VAR_MATRIX3X4:
+        SetShaderParameter(param, value.GetMatrix3x4());
+        break;
+        
+    case VAR_MATRIX4:
+        SetShaderParameter(param, value.GetMatrix4());
+        break;
+        
+    default:
+        // Unsupported parameter type, do nothing
+        break;
+    }
+}
+
+bool Graphics::NeedParameterUpdate(ShaderParameterGroup group, const void* source)
+{
+    if ((unsigned)(size_t)shaderParameterSources_[group] == M_MAX_UNSIGNED || shaderParameterSources_[group] != source)
+    {
+        shaderParameterSources_[group] = source;
+        return true;
+    }
+    else
+        return false;
+}
+
+bool Graphics::HasShaderParameter(StringHash param)
+{
+    return shaderProgram_ && shaderProgram_->parameters_.Find(param) != shaderProgram_->parameters_.End();
+}
+
+bool Graphics::HasTextureUnit(TextureUnit unit)
+{
+    return pixelShader_ && pixelShader_->HasTextureUnit(unit);
+}
+
+void Graphics::ClearParameterSource(ShaderParameterGroup group)
+{
+    shaderParameterSources_[group] = (const void*)M_MAX_UNSIGNED;
+}
+
+void Graphics::ClearParameterSources()
+{
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        shaderParameterSources_[i] = (const void*)M_MAX_UNSIGNED;
+}
+
+void Graphics::ClearTransformSources()
+{
+    shaderParameterSources_[SP_CAMERA] = (const void*)M_MAX_UNSIGNED;
+    shaderParameterSources_[SP_OBJECT] = (const void*)M_MAX_UNSIGNED;
+}
+
+void Graphics::SetTexture(unsigned index, Texture* texture)
+{
+    if (index >= MAX_TEXTURE_UNITS)
+        return;
+    
+    // Check if texture is currently bound as a rendertarget. In that case, use its backup texture, or blank if not defined
+    if (texture)
+    {
+        if (renderTargets_[0] && renderTargets_[0]->GetParentTexture() == texture)
+            texture = texture->GetBackupTexture();
+    }
+    
+    if (texture && texture->GetParametersDirty())
+    {
+        texture->UpdateParameters();
+        textures_[index] = 0; // Force reassign
+    }
+
+    if (texture != textures_[index])
+    {
+        if (firstDirtyTexture_ == M_MAX_UNSIGNED)
+            firstDirtyTexture_ = lastDirtyTexture_ = index;
+        else
+        {
+            if (index < firstDirtyTexture_)
+                firstDirtyTexture_ = index;
+            if (index > lastDirtyTexture_)
+                lastDirtyTexture_ = index;
+        }
+
+        textures_[index] = texture;
+        impl_->shaderResourceViews_[index] = texture ? (ID3D11ShaderResourceView*)texture->GetShaderResourceView() : 0;
+        impl_->samplers_[index] = texture ? (ID3D11SamplerState*)texture->GetSampler() : 0;
+        texturesDirty_ = true;
+    }
+}
+
+void Graphics::SetDefaultTextureFilterMode(TextureFilterMode mode)
+{
+    if (mode != defaultTextureFilterMode_)
+    {
+        defaultTextureFilterMode_ = mode;
+        SetTextureParametersDirty();
+    }
+}
+
+void Graphics::SetTextureAnisotropy(unsigned level)
+{
+    if (level != textureAnisotropy_)
+    {
+        textureAnisotropy_ = level;
+        SetTextureParametersDirty();
+    }
+}
+
+void Graphics::SetTextureParametersDirty()
+{
+    MutexLock lock(gpuObjectMutex_);
+
+    for (PODVector<GPUObject*>::Iterator i = gpuObjects_.Begin(); i != gpuObjects_.End(); ++i)
+    {
+        Texture* texture = dynamic_cast<Texture*>(*i);
+        if (texture)
+            texture->SetParametersDirty();
+    }
+}
+
+void Graphics::ResetRenderTargets()
+{
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        SetRenderTarget(i, (RenderSurface*)0);
+    SetDepthStencil((RenderSurface*)0);
+    SetViewport(IntRect(0, 0, width_, height_));
+}
+
+void Graphics::ResetRenderTarget(unsigned index)
+{
+    SetRenderTarget(index, (RenderSurface*)0);
+}
+
+void Graphics::ResetDepthStencil()
+{
+    SetDepthStencil((RenderSurface*)0);
+}
+
+void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
+{
+    if (index >= MAX_RENDERTARGETS)
+        return;
+    
+    if (renderTarget != renderTargets_[index])
+    {
+        renderTargets_[index] = renderTarget;
+        renderTargetsDirty_ = true;
+
+        // If the rendertarget is also bound as a texture, replace with backup texture or null
+        if (renderTarget)
+        {
+            Texture* parentTexture = renderTarget->GetParentTexture();
+            
+            for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+            {
+                if (textures_[i] == parentTexture)
+                    SetTexture(i, textures_[i]->GetBackupTexture());
+            }
+        }
+    }
+}
+
+void Graphics::SetRenderTarget(unsigned index, Texture2D* texture)
+{
+    RenderSurface* renderTarget = 0;
+    if (texture)
+        renderTarget = texture->GetRenderSurface();
+    
+    SetRenderTarget(index, renderTarget);
+}
+
+void Graphics::SetDepthStencil(RenderSurface* depthStencil)
+{
+    if (depthStencil != depthStencil_)
+    {
+        depthStencil_ = depthStencil;
+        renderTargetsDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthStencil(Texture2D* texture)
+{
+    RenderSurface* depthStencil = 0;
+    if (texture)
+        depthStencil = texture->GetRenderSurface();
+    
+    SetDepthStencil(depthStencil);
+    // Constant depth bias depends on the bitdepth
+    rasterizerStateDirty_ = true;
+}
+
+void Graphics::SetViewport(const IntRect& rect)
+{
+    IntVector2 size = GetRenderTargetDimensions();
+    
+    IntRect rectCopy = rect;
+    
+    if (rectCopy.right_ <= rectCopy.left_)
+        rectCopy.right_ = rectCopy.left_ + 1;
+    if (rectCopy.bottom_ <= rectCopy.top_)
+        rectCopy.bottom_ = rectCopy.top_ + 1;
+    rectCopy.left_ = Clamp(rectCopy.left_, 0, size.x_);
+    rectCopy.top_ = Clamp(rectCopy.top_, 0, size.y_);
+    rectCopy.right_ = Clamp(rectCopy.right_, 0, size.x_);
+    rectCopy.bottom_ = Clamp(rectCopy.bottom_, 0, size.y_);
+    
+    static D3D11_VIEWPORT d3dViewport;
+    d3dViewport.TopLeftX = (float)rectCopy.left_;
+    d3dViewport.TopLeftY = (float)rectCopy.top_;
+    d3dViewport.Width = (float)(rectCopy.right_ - rectCopy.left_);
+    d3dViewport.Height = (float)(rectCopy.bottom_ - rectCopy.top_);
+    d3dViewport.MinDepth = 0.0f;
+    d3dViewport.MaxDepth = 1.0f;
+
+    impl_->deviceContext_->RSSetViewports(1, &d3dViewport);
+
+    viewport_ = rectCopy;
+    
+    // Disable scissor test, needs to be re-enabled by the user
+    SetScissorTest(false);
+}
+
+void Graphics::SetBlendMode(BlendMode mode)
+{
+    if (mode != blendMode_)
+    {
+        blendMode_ = mode;
+        blendStateDirty_ = true;
+    }
+}
+
+void Graphics::SetColorWrite(bool enable)
+{
+    if (enable != colorWrite_)
+    {
+        colorWrite_ = enable;
+        blendStateDirty_ = true;
+    }
+}
+
+void Graphics::SetCullMode(CullMode mode)
+{
+    if (mode != cullMode_)
+    {
+        cullMode_ = mode;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthBias(float constantBias, float slopeScaledBias)
+{
+    if (constantBias != constantDepthBias_ || slopeScaledBias != slopeScaledDepthBias_)
+    {
+        constantDepthBias_ = constantBias;
+        slopeScaledDepthBias_ = slopeScaledBias;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthTest(CompareMode mode)
+{
+    if (mode != depthTestMode_)
+    {
+        depthTestMode_ = mode;
+        depthStateDirty_ = true;
+    }
+}
+
+void Graphics::SetDepthWrite(bool enable)
+{
+    if (enable != depthWrite_)
+    {
+        depthWrite_ = enable;
+        depthStateDirty_ = true;
+    }
+}
+
+void Graphics::SetFillMode(FillMode mode)
+{
+    if (mode != fillMode_)
+    {
+        fillMode_ = mode;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetScissorTest(bool enable, const Rect& rect, bool borderInclusive)
+{
+    // During some light rendering loops, a full rect is toggled on/off repeatedly.
+    // Disable scissor in that case to reduce state changes
+    if (rect.min_.x_ <= 0.0f && rect.min_.y_ <= 0.0f && rect.max_.x_ >= 1.0f && rect.max_.y_ >= 1.0f)
+        enable = false;
+    
+    if (enable)
+    {
+        IntVector2 rtSize(GetRenderTargetDimensions());
+        IntVector2 viewSize(viewport_.Size());
+        IntVector2 viewPos(viewport_.left_, viewport_.top_);
+        IntRect intRect;
+        int expand = borderInclusive ? 1 : 0;
+        
+        intRect.left_ = Clamp((int)((rect.min_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_, 0, rtSize.x_ - 1);
+        intRect.top_ = Clamp((int)((-rect.max_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_, 0, rtSize.y_ - 1);
+        intRect.right_ = Clamp((int)((rect.max_.x_ + 1.0f) * 0.5f * viewSize.x_) + viewPos.x_ + expand, 0, rtSize.x_);
+        intRect.bottom_ = Clamp((int)((-rect.min_.y_ + 1.0f) * 0.5f * viewSize.y_) + viewPos.y_ + expand, 0, rtSize.y_);
+        
+        if (intRect.right_ == intRect.left_)
+            intRect.right_++;
+        if (intRect.bottom_ == intRect.top_)
+            intRect.bottom_++;
+        
+        if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
+            enable = false;
+        
+        if (enable && intRect != scissorRect_)
+        {
+            scissorRect_ = intRect;
+            scissorRectDirty_ = true;
+        }
+    }
+    
+    if (enable != scissorTest_)
+    {
+        scissorTest_ = enable;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetScissorTest(bool enable, const IntRect& rect)
+{
+    IntVector2 rtSize(GetRenderTargetDimensions());
+    IntVector2 viewSize(viewport_.Size());
+    IntVector2 viewPos(viewport_.left_, viewport_.top_);
+    
+    if (enable)
+    {
+        IntRect intRect;
+        intRect.left_ = Clamp(rect.left_ + viewPos.x_, 0, rtSize.x_ - 1);
+        intRect.top_ = Clamp(rect.top_ + viewPos.y_, 0, rtSize.y_ - 1);
+        intRect.right_ = Clamp(rect.right_ + viewPos.x_, 0, rtSize.x_);
+        intRect.bottom_ = Clamp(rect.bottom_ + viewPos.y_, 0, rtSize.y_);
+        
+        if (intRect.right_ == intRect.left_)
+            intRect.right_++;
+        if (intRect.bottom_ == intRect.top_)
+            intRect.bottom_++;
+        
+        if (intRect.right_ < intRect.left_ || intRect.bottom_ < intRect.top_)
+            enable = false;
+        
+        if (enable && intRect != scissorRect_)
+        {
+            scissorRect_ = intRect;
+            scissorRectDirty_ = true;
+        }
+    }
+
+    if (enable != scissorTest_)
+    {
+        scissorTest_ = enable;
+        rasterizerStateDirty_ = true;
+    }
+}
+
+void Graphics::SetStencilTest(bool enable, CompareMode mode, StencilOp pass, StencilOp fail, StencilOp zFail, unsigned stencilRef, unsigned compareMask, unsigned writeMask)
+{
+    if (enable != stencilTest_)
+    {
+        stencilTest_ = enable;
+        depthStateDirty_ = true;
+    }
+    
+    if (enable)
+    {
+        if (mode != stencilTestMode_)
+        {
+            stencilTestMode_ = mode;
+            depthStateDirty_ = true;
+        }
+        if (pass != stencilPass_)
+        {
+            stencilPass_ = pass;
+            depthStateDirty_ = true;
+        }
+        if (fail != stencilFail_)
+        {
+            stencilFail_ = fail;
+            depthStateDirty_ = true;
+        }
+        if (zFail != stencilZFail_)
+        {
+            stencilZFail_ = zFail;
+            depthStateDirty_ = true;
+        }
+        if (compareMask != stencilCompareMask_)
+        {
+            stencilCompareMask_ = compareMask;
+            depthStateDirty_ = true;
+        }
+        if (writeMask != stencilWriteMask_)
+        {
+            stencilWriteMask_ = writeMask;
+            depthStateDirty_ = true;
+        }
+        if (stencilRef != stencilRef_)
+        {
+            stencilRef_ = stencilRef;
+            stencilRefDirty_ = true;
+            depthStateDirty_ = true;
+        }
+    }
+}
+
+void Graphics::SetClipPlane(bool enable, const Plane& clipPlane, const Matrix3x4& view, const Matrix4& projection)
+{
+    useClipPlane_ = enable;
+
+    if (enable)
+    {
+        Matrix4 viewProj = projection * view;
+        clipPlane_ = clipPlane.Transformed(viewProj).ToVector4();
+        SetShaderParameter(VSP_CLIPPLANE, clipPlane_);
+    }
+}
+
+void Graphics::BeginDumpShaders(const String& fileName)
+{
+    shaderPrecache_ = new ShaderPrecache(context_, fileName);
+}
+
+void Graphics::EndDumpShaders()
+{
+    shaderPrecache_.Reset();
+}
+
+void Graphics::PrecacheShaders(Deserializer& source)
+{
+    PROFILE(PrecacheShaders);
+    
+    ShaderPrecache::LoadShaders(this, source);
+}
+
+bool Graphics::IsInitialized() const
+{
+    return impl_->window_ != 0 && impl_->GetDevice() != 0;
+}
+
+IntVector2 Graphics::GetWindowPosition() const
+{
+    if (impl_->window_)
+        return position_;
+    return IntVector2::ZERO;
+}
+
+PODVector<IntVector2> Graphics::GetResolutions() const
+{
+    PODVector<IntVector2> ret;
+    unsigned numModes = SDL_GetNumDisplayModes(0);
+    
+    for (unsigned i = 0; i < numModes; ++i)
+    {
+        SDL_DisplayMode mode;
+        SDL_GetDisplayMode(0, i, &mode);
+        int width = mode.w;
+        int height  = mode.h;
+        
+        // Store mode if unique
+        bool unique = true;
+        for (unsigned j = 0; j < ret.Size(); ++j)
+        {
+            if (ret[j].x_ == width && ret[j].y_ == height)
+            {
+                unique = false;
+                break;
+            }
+        }
+        
+        if (unique)
+            ret.Push(IntVector2(width, height));
+    }
+    
+    return ret;
+}
+
+PODVector<int> Graphics::GetMultiSampleLevels() const
+{
+    PODVector<int> ret;
+    ret.Push(1);
+
+    if (impl_->device_)
+    {
+        for (unsigned i = 2; i <= 16; ++i)
+        {
+            unsigned levels = 0;
+            impl_->device_->CheckMultisampleQualityLevels(sRGB_ ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM,
+                i, &levels);
+            if (levels)
+                ret.Push(i);
+        }
+    }
+
+    return ret;
+}
+
+IntVector2 Graphics::GetDesktopResolution() const
+{
+    SDL_DisplayMode mode;
+    SDL_GetDesktopDisplayMode(0, &mode);
+    return IntVector2(mode.w, mode.h);
+}
+
+unsigned Graphics::GetFormat(CompressedFormat format) const
+{
+    switch (format)
+    {
+    case CF_RGBA:
+        return DXGI_FORMAT_R8G8B8A8_UNORM;
+
+    case CF_DXT1:
+        return DXGI_FORMAT_BC1_UNORM;
+        
+    case CF_DXT3:
+        return DXGI_FORMAT_BC2_UNORM;
+        
+    case CF_DXT5:
+        return DXGI_FORMAT_BC3_UNORM;
+    }
+    
+    return 0;
+}
+
+ShaderVariation* Graphics::GetShader(ShaderType type, const String& name, const String& defines) const
+{
+    return GetShader(type, name.CString(), defines.CString());
+}
+
+ShaderVariation* Graphics::GetShader(ShaderType type, const char* name, const char* defines) const
+{
+    if (lastShaderName_ != name || !lastShader_)
+    {
+        ResourceCache* cache = GetSubsystem<ResourceCache>();
+        
+        String fullShaderName = shaderPath_ + name + shaderExtension_;
+        // Try to reduce repeated error log prints because of missing shaders
+        if (lastShaderName_ == name && !cache->Exists(fullShaderName))
+            return 0;
+        
+        lastShader_ = cache->GetResource<Shader>(fullShaderName);
+        lastShaderName_ = name;
+    }
+    
+    return lastShader_ ? lastShader_->GetVariation(type, defines) : (ShaderVariation*)0;
+}
+
+VertexBuffer* Graphics::GetVertexBuffer(unsigned index) const
+{
+    return index < MAX_VERTEX_STREAMS ? vertexBuffers_[index] : 0;
+}
+
+TextureUnit Graphics::GetTextureUnit(const String& name)
+{
+    HashMap<String, TextureUnit>::Iterator i = textureUnits_.Find(name);
+    if (i != textureUnits_.End())
+        return i->second_;
+    else
+        return MAX_TEXTURE_UNITS;
+}
+
+const String& Graphics::GetTextureUnitName(TextureUnit unit)
+{
+    for (HashMap<String, TextureUnit>::Iterator i = textureUnits_.Begin(); i != textureUnits_.End(); ++i)
+    {
+        if (i->second_ == unit)
+            return i->first_;
+    }
+    return String::EMPTY;
+}
+
+Texture* Graphics::GetTexture(unsigned index) const
+{
+    return index < MAX_TEXTURE_UNITS ? textures_[index] : 0;
+}
+
+RenderSurface* Graphics::GetRenderTarget(unsigned index) const
+{
+    return index < MAX_RENDERTARGETS ? renderTargets_[index] : 0;
+}
+
+IntVector2 Graphics::GetRenderTargetDimensions() const
+{
+    int width, height;
+    
+    if (renderTargets_[0])
+    {
+        width = renderTargets_[0]->GetWidth();
+        height = renderTargets_[0]->GetHeight();
+    }
+    else if (depthStencil_) // Depth-only rendering
+    {
+        width = depthStencil_->GetWidth();
+        height = depthStencil_->GetHeight();
+    }
+    else
+    {
+        width = width_;
+        height = height_;
+    }
+    
+    return IntVector2(width, height);
+}
+
+void Graphics::WindowResized()
+{
+    if (!impl_->device_ || !impl_->window_)
+        return;
+    
+    int newWidth, newHeight;
+    
+    SDL_GetWindowSize(impl_->window_, &newWidth, &newHeight);
+    if (newWidth == width_ && newHeight == height_)
+        return;
+
+    UpdateSwapChain(newWidth, newHeight);
+    
+    // Reset rendertargets and viewport for the new screen size
+    ResetRenderTargets();
+    
+    LOGDEBUGF("Window was resized to %dx%d", width_, height_);
+    
+    using namespace ScreenMode;
+    
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_WIDTH] = width_;
+    eventData[P_HEIGHT] = height_;
+    eventData[P_FULLSCREEN] = fullscreen_;
+    eventData[P_RESIZABLE] = resizable_;
+    eventData[P_BORDERLESS] = borderless_;
+    SendEvent(E_SCREENMODE, eventData);
+}
+
+void Graphics::WindowMoved()
+{
+    if (!impl_->device_ || !impl_->window_ || fullscreen_)
+        return;
+
+    int newX, newY;
+
+    SDL_GetWindowPosition(impl_->window_, &newX, &newY);
+    if (newX == position_.x_ && newY == position_.y_)
+        return;
+
+    position_.x_ = newX;
+    position_.y_ = newY;
+
+    LOGDEBUGF("Window was moved to %d,%d", position_.x_, position_.y_);
+
+    using namespace WindowPos;
+
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_X] = position_.x_;
+    eventData[P_Y] = position_.y_;
+    SendEvent(E_WINDOWPOS, eventData);
+}
+
+void Graphics::Maximize()
+{
+    if (!impl_->window_)
+        return;
+
+    SDL_MaximizeWindow(impl_->window_);
+}
+
+void Graphics::Minimize()
+{
+    if (!impl_->window_)
+        return;
+
+    SDL_MinimizeWindow(impl_->window_);
+}
+
+void Graphics::AddGPUObject(GPUObject* object)
+{
+    MutexLock lock(gpuObjectMutex_);
+
+    gpuObjects_.Push(object);
+}
+
+void Graphics::RemoveGPUObject(GPUObject* object)
+{
+    MutexLock lock(gpuObjectMutex_);
+
+    gpuObjects_.Remove(object);
+}
+
+void* Graphics::ReserveScratchBuffer(unsigned size)
+{
+    if (!size)
+        return 0;
+    
+    if (size > maxScratchBufferRequest_)
+        maxScratchBufferRequest_ = size;
+    
+    // First check for a free buffer that is large enough
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (!i->reserved_ && i->size_ >= size)
+        {
+            i->reserved_ = true;
+            return i->data_.Get();
+        }
+    }
+    
+    // Then check if a free buffer can be resized
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (!i->reserved_)
+        {
+            i->data_ = new unsigned char[size];
+            i->size_ = size;
+            i->reserved_ = true;
+            
+            LOGDEBUG("Resized scratch buffer to size " + String(size));
+            
+            return i->data_.Get();
+        }
+    }
+    
+    // Finally allocate a new buffer
+    ScratchBuffer newBuffer;
+    newBuffer.data_ = new unsigned char[size];
+    newBuffer.size_ = size;
+    newBuffer.reserved_ = true;
+    scratchBuffers_.Push(newBuffer);
+    return newBuffer.data_.Get();
+    
+    LOGDEBUG("Allocated scratch buffer with size " + String(size));
+}
+
+void Graphics::FreeScratchBuffer(void* buffer)
+{
+    if (!buffer)
+        return;
+    
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (i->reserved_ && i->data_.Get() == buffer)
+        {
+            i->reserved_ = false;
+            return;
+        }
+    }
+
+    LOGWARNING("Reserved scratch buffer " + ToStringHex((unsigned)(size_t)buffer) + " not found");
+}
+
+void Graphics::CleanupScratchBuffers()
+{
+    for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
+    {
+        if (!i->reserved_ && i->size_ > maxScratchBufferRequest_ * 2)
+        {
+            i->data_ = maxScratchBufferRequest_ > 0 ? new unsigned char[maxScratchBufferRequest_] : 0;
+            i->size_ = maxScratchBufferRequest_;
+            
+            LOGDEBUG("Resized scratch buffer to size " + String(maxScratchBufferRequest_));
+        }
+    }
+    
+    maxScratchBufferRequest_ = 0;
+}
+
+void Graphics::CleanUpShaderPrograms(ShaderVariation* variation)
+{
+    for (ShaderProgramMap::Iterator i = shaderPrograms_.Begin(); i != shaderPrograms_.End();)
+    {
+        if (i->first_.first_ == variation || i->first_.second_ == variation)
+            i = shaderPrograms_.Erase(i);
+        else
+            ++i;
+    }
+
+    if (vertexShader_ == variation || pixelShader_ == variation)
+        shaderProgram_ = 0;
+}
+
+ConstantBuffer* Graphics::GetOrCreateConstantBuffer(ShaderType type, unsigned index, unsigned size)
+{
+    // Ensure that different shader types and index slots get unique buffers, even if the size is same
+    unsigned key = type | (index << 1) | (size << 4);
+    HashMap<unsigned, SharedPtr<ConstantBuffer> >::Iterator i = constantBuffers_.Find(key);
+    if (i != constantBuffers_.End())
+        return i->second_.Get();
+    else
+    {
+        SharedPtr<ConstantBuffer> newConstantBuffer(new ConstantBuffer(context_));
+        newConstantBuffer->SetSize(size);
+        constantBuffers_[key] = newConstantBuffer;
+        return newConstantBuffer.Get();
+    }
+}
+
+unsigned Graphics::GetAlphaFormat()
+{
+    return DXGI_FORMAT_A8_UNORM;
+}
+
+unsigned Graphics::GetLuminanceFormat()
+{
+    // Note: not same sampling behavior as on D3D9; need to sample the R channel only
+    return DXGI_FORMAT_R8_UNORM; 
+}
+
+unsigned Graphics::GetLuminanceAlphaFormat()
+{
+    // Note: not same sampling behavior as on D3D9; need to sample the RG channels
+    return DXGI_FORMAT_R8G8_UNORM;
+}
+
+unsigned Graphics::GetRGBFormat()
+{
+    return DXGI_FORMAT_R8G8B8A8_UNORM;
+}
+
+unsigned Graphics::GetRGBAFormat()
+{
+    return DXGI_FORMAT_R8G8B8A8_UNORM;
+}
+
+unsigned Graphics::GetRGBA16Format()
+{
+    return DXGI_FORMAT_R16G16B16A16_UNORM;
+}
+
+unsigned Graphics::GetRGBAFloat16Format()
+{
+    return DXGI_FORMAT_R16G16B16A16_FLOAT;
+}
+
+unsigned Graphics::GetRGBAFloat32Format()
+{
+    return DXGI_FORMAT_R32G32B32A32_FLOAT;
+}
+
+unsigned Graphics::GetRG16Format()
+{
+    return DXGI_FORMAT_R16G16_UNORM;
+}
+
+unsigned Graphics::GetRGFloat16Format()
+{
+    return DXGI_FORMAT_R16G16_FLOAT;
+}
+
+unsigned Graphics::GetRGFloat32Format()
+{
+    return DXGI_FORMAT_R32G32_FLOAT;
+}
+
+unsigned Graphics::GetFloat16Format()
+{
+    return DXGI_FORMAT_R16_FLOAT;
+}
+
+unsigned Graphics::GetFloat32Format()
+{
+    return DXGI_FORMAT_R32_FLOAT;
+}
+
+unsigned Graphics::GetLinearDepthFormat()
+{
+    return DXGI_FORMAT_R32_FLOAT;
+}
+
+unsigned Graphics::GetDepthStencilFormat()
+{
+    return DXGI_FORMAT_R24G8_TYPELESS;
+}
+
+unsigned Graphics::GetReadableDepthFormat()
+{
+    return DXGI_FORMAT_R24G8_TYPELESS;
+}
+
+unsigned Graphics::GetFormat(const String& formatName)
+{
+    String nameLower = formatName.ToLower().Trimmed();
+    
+    if (nameLower == "a")
+        return GetAlphaFormat();
+    if (nameLower == "l")
+        return GetLuminanceFormat();
+    if (nameLower == "la")
+        return GetLuminanceAlphaFormat();
+    if (nameLower == "rgb")
+        return GetRGBFormat();
+    if (nameLower == "rgba")
+        return GetRGBAFormat();
+    if (nameLower == "rgba16")
+        return GetRGBA16Format();
+    if (nameLower == "rgba16f")
+        return GetRGBAFloat16Format();
+    if (nameLower == "rgba32f")
+        return GetRGBAFloat32Format();
+    if (nameLower == "rg16")
+        return GetRG16Format();
+    if (nameLower == "rg16f")
+        return GetRGFloat16Format();
+    if (nameLower == "rg32f")
+        return GetRGFloat32Format();
+    if (nameLower == "r16f")
+        return GetFloat16Format();
+    if (nameLower == "r32f" || nameLower == "float")
+        return GetFloat32Format();
+    if (nameLower == "lineardepth" || nameLower == "depth")
+        return GetLinearDepthFormat();
+    if (nameLower == "d24s8")
+        return GetDepthStencilFormat();
+    if (nameLower == "readabledepth" || nameLower == "hwdepth")
+        return GetReadableDepthFormat();
+    
+    return GetRGBFormat();
+}
+
+bool Graphics::OpenWindow(int width, int height, bool resizable, bool borderless)
+{
+    if (!externalWindow_)
+    {
+        unsigned flags = 0;
+        if (resizable)
+            flags |= SDL_WINDOW_RESIZABLE;
+        if (borderless)
+            flags |= SDL_WINDOW_BORDERLESS;
+
+        impl_->window_ = SDL_CreateWindow(windowTitle_.CString(), position_.x_, position_.y_, width, height, flags);
+    }
+    else
+        impl_->window_ = SDL_CreateWindowFrom(externalWindow_, 0);
+
+    if (!impl_->window_)
+    {
+        LOGERROR("Could not create window");
+        return false;
+    }
+
+    SDL_GetWindowPosition(impl_->window_, &position_.x_, &position_.y_);
+
+    CreateWindowIcon();
+
+    return true;
+}
+
+void Graphics::CreateWindowIcon()
+{
+    if (windowIcon_)
+    {
+        SDL_Surface* surface = windowIcon_->GetSDLSurface();
+        if (surface)
+        {
+            SDL_SetWindowIcon(impl_->window_, surface);
+            SDL_FreeSurface(surface);
+        }
+    }
+}
+
+void Graphics::AdjustWindow(int& newWidth, int& newHeight, bool& newFullscreen, bool& newBorderless)
+{
+    if (!externalWindow_)
+    {
+        if (!newWidth || !newHeight)
+        {
+            SDL_MaximizeWindow(impl_->window_);
+            SDL_GetWindowSize(impl_->window_, &newWidth, &newHeight);
+        }
+        else
+            SDL_SetWindowSize(impl_->window_, newWidth, newHeight);
+
+        SDL_SetWindowFullscreen(impl_->window_, newFullscreen ? SDL_TRUE : SDL_FALSE);
+        SDL_SetWindowBordered(impl_->window_, newBorderless ? SDL_FALSE : SDL_TRUE);
+    }
+    else
+    {
+        // If external window, must ask its dimensions instead of trying to set them
+        SDL_GetWindowSize(impl_->window_, &newWidth, &newHeight);
+        newFullscreen = false;
+    }
+}
+
+bool Graphics::CreateDevice(int width, int height, int multiSample)
+{
+    // Device needs only to be created once
+    if (!impl_->device_)
+    {
+        D3D11CreateDevice(
+            0,
+            D3D_DRIVER_TYPE_HARDWARE,
+            0,
+            0,
+            0,
+            0,
+            D3D11_SDK_VERSION,
+            &impl_->device_,
+            0,
+            &impl_->deviceContext_
+        );
+
+        if (!impl_->device_ || !impl_->deviceContext_)
+        {
+            LOGERROR("Failed to create D3D11 device");
+            return false;
+        }
+
+        CheckFeatureSupport();
+        // Set the flush mode now as the device has been created
+        SetFlushGPU(flushGPU_);
+    }
+
+    // Check that multisample level is supported
+    PODVector<int> multiSampleLevels = GetMultiSampleLevels();
+    if (!multiSampleLevels.Contains(multiSample))
+        multiSample = 1;
+
+    // Create swap chain. Release old if necessary
+    if (impl_->swapChain_)
+    {
+        impl_->swapChain_->Release();
+        impl_->swapChain_ = 0;
+    }
+
+    DXGI_SWAP_CHAIN_DESC swapChainDesc;
+    memset(&swapChainDesc, 0, sizeof swapChainDesc);
+    swapChainDesc.BufferCount = 1;
+    swapChainDesc.BufferDesc.Width = width;
+    swapChainDesc.BufferDesc.Height = height;
+    swapChainDesc.BufferDesc.Format = sRGB_ ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    swapChainDesc.OutputWindow = GetWindowHandle(impl_->window_);
+    swapChainDesc.SampleDesc.Count = multiSample;
+    swapChainDesc.SampleDesc.Quality = multiSample > 1 ? 0xffffffff : 0;
+    swapChainDesc.Windowed = TRUE;
+    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+    IDXGIDevice* dxgiDevice = 0;
+    impl_->device_->QueryInterface(IID_IDXGIDevice, (void **)&dxgiDevice);
+    IDXGIAdapter* dxgiAdapter = 0;
+    dxgiDevice->GetParent(IID_IDXGIAdapter, (void **)&dxgiAdapter);
+    IDXGIFactory* dxgiFactory = 0;
+    dxgiAdapter->GetParent(IID_IDXGIFactory, (void **)&dxgiFactory);
+    dxgiFactory->CreateSwapChain(impl_->device_, &swapChainDesc, &impl_->swapChain_);
+    // After creating the swap chain, disable automatic Alt-Enter fullscreen/windowed switching
+    // (the application will switch manually if it wants to)
+    dxgiFactory->MakeWindowAssociation(GetWindowHandle(impl_->window_), DXGI_MWA_NO_ALT_ENTER);
+
+    dxgiFactory->Release();
+    dxgiAdapter->Release();
+    dxgiDevice->Release();
+
+    if (impl_->swapChain_)
+    {
+        multiSample_ = multiSample;
+        return true;
+    }
+    else
+    {
+        LOGERROR("Failed to create D3D11 swap chain");
+        return false;
+    }
+}
+
+bool Graphics::UpdateSwapChain(int width, int height)
+{
+    bool success = true;
+
+    ID3D11RenderTargetView* nullView = 0;
+    impl_->deviceContext_->OMSetRenderTargets(1, &nullView, 0);
+    if (impl_->defaultRenderTargetView_)
+    {
+        impl_->defaultRenderTargetView_->Release();
+        impl_->defaultRenderTargetView_ = 0;
+    }
+    if (impl_->defaultDepthStencilView_)
+    {
+        impl_->defaultDepthStencilView_->Release();
+        impl_->defaultDepthStencilView_ = 0;
+    }
+    if (impl_->defaultDepthTexture_)
+    {
+        impl_->defaultDepthTexture_->Release();
+        impl_->defaultDepthTexture_ = 0;
+    }
+
+    impl_->depthStencilView_ = 0;
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        impl_->renderTargetViews_[i] = 0;
+    renderTargetsDirty_ = true;
+
+    impl_->swapChain_->ResizeBuffers(1, width, height, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
+
+    // Create default rendertarget view representing the backbuffer
+    ID3D11Texture2D* backbufferTexture;
+    impl_->swapChain_->GetBuffer(0, IID_ID3D11Texture2D, (void**)&backbufferTexture);
+    if (backbufferTexture)
+    {
+        impl_->device_->CreateRenderTargetView(backbufferTexture, 0, &impl_->defaultRenderTargetView_);
+        backbufferTexture->Release();
+    }
+    else
+    {
+        LOGERROR("Failed to get backbuffer texture");
+        success = false;
+    }
+
+    // Create default depth-stencil texture and view
+    D3D11_TEXTURE2D_DESC depthDesc;
+    memset(&depthDesc, 0, sizeof depthDesc);
+    depthDesc.Width = width;
+    depthDesc.Height = height;
+    depthDesc.MipLevels = 1;
+    depthDesc.ArraySize = 1;
+    depthDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
+    depthDesc.SampleDesc.Count = multiSample_;
+    depthDesc.SampleDesc.Quality = multiSample_ > 1 ? 0xffffffff : 0;
+    depthDesc.Usage = D3D11_USAGE_DEFAULT;
+    depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
+    depthDesc.CPUAccessFlags = 0;
+    depthDesc.MiscFlags = 0;
+    impl_->device_->CreateTexture2D(&depthDesc, 0, &impl_->defaultDepthTexture_);
+    if (impl_->defaultDepthTexture_)
+        impl_->device_->CreateDepthStencilView(impl_->defaultDepthTexture_, 0, &impl_->defaultDepthStencilView_);
+    else
+    {
+        LOGERROR("Failed to create backbuffer depth-stencil texture");
+        success = false;
+    }
+
+    // Update internally held backbuffer size
+    width_ = width;
+    height_ = height;
+
+    ResetRenderTargets();
+    return success;
+}
+
+void Graphics::CheckFeatureSupport()
+{
+    lightPrepassSupport_ = true;
+    deferredSupport_ = true;
+    hardwareShadowSupport_ = true;
+    instancingSupport_ = true;
+    shadowMapFormat_ = DXGI_FORMAT_R16_TYPELESS;
+    hiresShadowMapFormat_ = DXGI_FORMAT_R32_TYPELESS;
+    dummyColorFormat_ = DXGI_FORMAT_UNKNOWN;
+    sRGBSupport_ = true;
+    sRGBWriteSupport_ = true;
+}
+
+void Graphics::ResetCachedState()
+{
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        vertexBuffers_[i] = 0;
+        elementMasks_[i] = 0;
+        impl_->vertexBuffers_[i] = 0;
+        impl_->vertexSizes_[i] = 0;
+        impl_->vertexOffsets_[i] = 0;
+    }
+    
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        textures_[i] = 0;
+        impl_->shaderResourceViews_[i] = 0;
+        impl_->samplers_[i] = 0;
+    }
+
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+    {
+        renderTargets_[i] = 0;
+        impl_->renderTargetViews_[i] = 0;
+    }
+
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+    {
+        impl_->constantBuffers_[VS][i] = 0;
+        impl_->constantBuffers_[PS][i] = 0;
+    }
+    
+    depthStencil_ = 0;
+    impl_->depthStencilView_ = 0;
+    viewport_ = IntRect(0, 0, width_, height_);
+    
+    indexBuffer_ = 0;
+    vertexDeclarationHash_ = 0;
+    primitiveType_ = 0;
+    vertexShader_ = 0;
+    pixelShader_ = 0;
+    shaderProgram_ = 0;
+    blendMode_ = BLEND_REPLACE;
+    textureAnisotropy_ = 1;
+    colorWrite_ = true;
+    cullMode_ = CULL_CCW;
+    constantDepthBias_ = 0.0f;
+    slopeScaledDepthBias_ = 0.0f;
+    depthTestMode_ = CMP_LESSEQUAL;
+    depthWrite_ = true;
+    fillMode_ = FILL_SOLID;
+    scissorTest_ = false;
+    scissorRect_ = IntRect::ZERO;
+    stencilTest_ = false;
+    stencilTestMode_ = CMP_ALWAYS;
+    stencilPass_ = OP_KEEP;
+    stencilFail_ = OP_KEEP;
+    stencilZFail_ = OP_KEEP;
+    stencilRef_ = 0;
+    stencilCompareMask_ = M_MAX_UNSIGNED;
+    stencilWriteMask_ = M_MAX_UNSIGNED;
+    useClipPlane_ = false;
+    renderTargetsDirty_ = true;
+    texturesDirty_ = true;
+    vertexDeclarationDirty_ = true;
+    blendStateDirty_ = true;
+    depthStateDirty_ = true;
+    rasterizerStateDirty_ = true;
+    scissorRectDirty_ = true;
+    stencilRefDirty_ = true;
+    blendStateHash_ = M_MAX_UNSIGNED;
+    depthStateHash_ = M_MAX_UNSIGNED;
+    rasterizerStateHash_ = M_MAX_UNSIGNED;
+    firstDirtyTexture_ = lastDirtyTexture_ = M_MAX_UNSIGNED;
+    firstDirtyVB_ = lastDirtyVB_ = M_MAX_UNSIGNED;
+    dirtyConstantBuffers_.Clear();
+}
+
+void Graphics::PrepareDraw()
+{
+    if (renderTargetsDirty_)
+    {
+        impl_->depthStencilView_ = depthStencil_ ? (ID3D11DepthStencilView*)depthStencil_->GetRenderTargetView() : impl_->defaultDepthStencilView_;
+
+        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+            impl_->renderTargetViews_[i] = renderTargets_[i] ? (ID3D11RenderTargetView*)renderTargets_[i]->GetRenderTargetView() : 0;
+        // If rendertarget 0 is null and not doing depth-only rendering, render to the backbuffer
+        // Special case: if rendertarget 0 is null and depth stencil has same size as backbuffer, assume the intention is to do
+        // backbuffer rendering with a custom depth stencil
+        if (!renderTargets_[0] && (!depthStencil_ || (depthStencil_ && depthStencil_->GetWidth() == width_ && depthStencil_->GetHeight() == height_)))
+            impl_->renderTargetViews_[0] = impl_->defaultRenderTargetView_;
+        
+        impl_->deviceContext_->OMSetRenderTargets(MAX_RENDERTARGETS, &impl_->renderTargetViews_[0], impl_->depthStencilView_);
+        renderTargetsDirty_ = false;
+    }
+
+    if (texturesDirty_ && firstDirtyTexture_ < M_MAX_UNSIGNED)
+    {
+        // Set also VS textures to enable vertex texture fetch to work the same way as on OpenGL
+        impl_->deviceContext_->VSSetShaderResources(firstDirtyTexture_, lastDirtyTexture_ - firstDirtyTexture_ + 1,
+            &impl_->shaderResourceViews_[firstDirtyTexture_]);
+        impl_->deviceContext_->VSSetSamplers(firstDirtyTexture_, lastDirtyTexture_ - firstDirtyTexture_ + 1,
+            &impl_->samplers_[firstDirtyTexture_]);
+        impl_->deviceContext_->PSSetShaderResources(firstDirtyTexture_, lastDirtyTexture_ - firstDirtyTexture_ + 1,
+            &impl_->shaderResourceViews_[firstDirtyTexture_]);
+        impl_->deviceContext_->PSSetSamplers(firstDirtyTexture_, lastDirtyTexture_ - firstDirtyTexture_ + 1,
+            &impl_->samplers_[firstDirtyTexture_]);
+
+        firstDirtyTexture_ = lastDirtyTexture_ = M_MAX_UNSIGNED;
+        texturesDirty_ = false;
+    }
+
+    if (vertexDeclarationDirty_ && vertexShader_ && vertexShader_->GetByteCode().Size())
+    {
+        if (firstDirtyVB_ < M_MAX_UNSIGNED)
+        {
+            impl_->deviceContext_->IASetVertexBuffers(firstDirtyVB_, lastDirtyVB_ - firstDirtyVB_ + 1, &impl_->vertexBuffers_
+                [firstDirtyVB_], &impl_->vertexSizes_[firstDirtyVB_], &impl_->vertexOffsets_[firstDirtyVB_]);
+
+            firstDirtyVB_ = lastDirtyVB_ = M_MAX_UNSIGNED;
+        }
+
+        unsigned long long newVertexDeclarationHash = 0;
+        for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+            newVertexDeclarationHash |= (unsigned long long)elementMasks_[i] << (i * 13);
+
+        // Do not create input layout if no vertex buffers / elements
+        if (newVertexDeclarationHash)
+        {
+            newVertexDeclarationHash |= (unsigned long long)vertexShader_->GetElementMask() << 51;
+            if (newVertexDeclarationHash != vertexDeclarationHash_)
+            {
+                HashMap<unsigned long long, SharedPtr<VertexDeclaration> >::Iterator i = vertexDeclarations_.Find(newVertexDeclarationHash);
+                if (i == vertexDeclarations_.End())
+                {
+                    SharedPtr<VertexDeclaration> newVertexDeclaration(new VertexDeclaration(this, vertexShader_, vertexBuffers_,
+                        elementMasks_));
+                    i = vertexDeclarations_.Insert(MakePair(newVertexDeclarationHash, newVertexDeclaration));
+                }
+    
+                impl_->deviceContext_->IASetInputLayout((ID3D11InputLayout*)i->second_->GetInputLayout());
+                vertexDeclarationHash_ = newVertexDeclarationHash;
+            }
+        }
+
+        vertexDeclarationDirty_ = false;
+    }
+
+    if (blendStateDirty_)
+    {
+        unsigned newBlendStateHash = (colorWrite_ ? 1 : 0) | (blendMode_ << 1);
+        if (newBlendStateHash != blendStateHash_)
+        {
+            HashMap<unsigned, ID3D11BlendState*>::Iterator i = impl_->blendStates_.Find(newBlendStateHash);
+            if (i == impl_->blendStates_.End())
+            {
+                PROFILE(CreateBlendState);
+
+                D3D11_BLEND_DESC stateDesc;
+                memset(&stateDesc, 0, sizeof stateDesc);
+                stateDesc.AlphaToCoverageEnable = false;
+                stateDesc.IndependentBlendEnable = false;
+                stateDesc.RenderTarget[0].BlendEnable = d3dBlendEnable[blendMode_];
+                stateDesc.RenderTarget[0].SrcBlend = d3dSrcBlend[blendMode_];
+                stateDesc.RenderTarget[0].DestBlend = d3dDestBlend[blendMode_];
+                stateDesc.RenderTarget[0].BlendOp = d3dBlendOp[blendMode_];
+                stateDesc.RenderTarget[0].SrcBlendAlpha = d3dSrcBlend[blendMode_];
+                stateDesc.RenderTarget[0].DestBlendAlpha = d3dDestBlend[blendMode_];
+                stateDesc.RenderTarget[0].BlendOpAlpha = d3dBlendOp[blendMode_];
+                stateDesc.RenderTarget[0].RenderTargetWriteMask = colorWrite_ ? D3D11_COLOR_WRITE_ENABLE_ALL : 0x0;
+
+                ID3D11BlendState* newBlendState = 0;
+                impl_->device_->CreateBlendState(&stateDesc, &newBlendState);
+                if (!newBlendState)
+                    LOGERROR("Failed to create blend state");
+
+                i = impl_->blendStates_.Insert(MakePair(newBlendStateHash, newBlendState));
+            }
+
+            impl_->deviceContext_->OMSetBlendState(i->second_, 0, M_MAX_UNSIGNED);
+            blendStateHash_ = newBlendStateHash;
+        }
+
+        blendStateDirty_ = false;
+    }
+
+    if (depthStateDirty_)
+    {
+        unsigned newDepthStateHash = (depthWrite_ ? 1 : 0) | (stencilTest_ ? 2 : 0) | (depthTestMode_ << 2) |
+            ((stencilCompareMask_ & 0xff) << 5) | ((stencilWriteMask_ & 0xff) << 13) | (stencilTestMode_ << 21) |
+            ((stencilFail_ + stencilZFail_ * 5 + stencilPass_ * 25) << 24);
+        if (newDepthStateHash != depthStateHash_ || stencilRefDirty_)
+        {
+            HashMap<unsigned, ID3D11DepthStencilState*>::Iterator i = impl_->depthStates_.Find(newDepthStateHash);
+            if (i == impl_->depthStates_.End())
+            {
+                PROFILE(CreateDepthState);
+
+                D3D11_DEPTH_STENCIL_DESC stateDesc;
+                memset(&stateDesc, 0, sizeof stateDesc);
+                stateDesc.DepthEnable = TRUE;
+                stateDesc.DepthWriteMask = depthWrite_ ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
+                stateDesc.DepthFunc = d3dCmpFunc[depthTestMode_];
+                stateDesc.StencilEnable = stencilTest_ ? TRUE : FALSE;
+                stateDesc.StencilReadMask = (unsigned char)stencilCompareMask_;
+                stateDesc.StencilWriteMask = (unsigned char)stencilWriteMask_;
+                stateDesc.FrontFace.StencilFailOp = d3dStencilOp[stencilFail_];
+                stateDesc.FrontFace.StencilDepthFailOp = d3dStencilOp[stencilZFail_];
+                stateDesc.FrontFace.StencilPassOp = d3dStencilOp[stencilPass_];
+                stateDesc.FrontFace.StencilFunc = d3dCmpFunc[stencilTestMode_];
+                stateDesc.BackFace.StencilFailOp = d3dStencilOp[stencilFail_];
+                stateDesc.BackFace.StencilDepthFailOp = d3dStencilOp[stencilZFail_];
+                stateDesc.BackFace.StencilPassOp = d3dStencilOp[stencilPass_];
+                stateDesc.BackFace.StencilFunc = d3dCmpFunc[stencilTestMode_];
+
+                ID3D11DepthStencilState* newDepthState = 0;
+                impl_->device_->CreateDepthStencilState(&stateDesc, &newDepthState);
+                if (!newDepthState)
+                    LOGERROR("Failed to create depth state");
+
+                i = impl_->depthStates_.Insert(MakePair(newDepthStateHash, newDepthState));
+            }
+
+            impl_->deviceContext_->OMSetDepthStencilState(i->second_, stencilRef_);
+            depthStateHash_ = newDepthStateHash;
+        }
+        
+        depthStateDirty_ = false;
+        stencilRefDirty_ = false;
+    }
+
+    if (rasterizerStateDirty_)
+    {
+        unsigned depthBits = 24;
+        if (depthStencil_ && depthStencil_->GetParentTexture()->GetFormat() == DXGI_FORMAT_R16_TYPELESS)
+            depthBits = 16;
+        int scaledDepthBias = (int)(constantDepthBias_ * (1 << depthBits));
+
+        unsigned newRasterizerStateHash = (scissorTest_ ? 1 : 0) | (fillMode_ << 1) | (cullMode_ << 3) |
+            ((scaledDepthBias & 0x1fff) << 5) | ((*((unsigned*)&slopeScaledDepthBias_) & 0x1fff) << 18);
+        if (newRasterizerStateHash != rasterizerStateHash_)
+        {
+            HashMap<unsigned, ID3D11RasterizerState*>::Iterator i = impl_->rasterizerStates_.Find(newRasterizerStateHash);
+            if (i == impl_->rasterizerStates_.End())
+            {
+                PROFILE(CreateRasterizerState);
+
+                D3D11_RASTERIZER_DESC stateDesc;
+                memset(&stateDesc, 0, sizeof stateDesc);
+                stateDesc.FillMode = d3dFillMode[fillMode_];
+                stateDesc.CullMode = d3dCullMode[cullMode_];
+                stateDesc.FrontCounterClockwise = FALSE;
+                stateDesc.DepthBias = scaledDepthBias;
+                stateDesc.DepthBiasClamp = M_INFINITY;
+                stateDesc.SlopeScaledDepthBias = slopeScaledDepthBias_;
+                stateDesc.DepthClipEnable = TRUE;
+                stateDesc.ScissorEnable = scissorTest_ ? TRUE : FALSE;
+                stateDesc.MultisampleEnable = TRUE;
+                stateDesc.AntialiasedLineEnable = FALSE;
+
+                ID3D11RasterizerState* newRasterizerState = 0;
+                impl_->device_->CreateRasterizerState(&stateDesc, &newRasterizerState);
+                if (!newRasterizerState)
+                    LOGERROR("Failed to create rasterizer state");
+
+                i = impl_->rasterizerStates_.Insert(MakePair(newRasterizerStateHash, newRasterizerState));
+            }
+
+            impl_->deviceContext_->RSSetState(i->second_);
+            rasterizerStateHash_ = newRasterizerStateHash;
+        }
+
+        rasterizerStateDirty_ = false;
+    }
+
+    if (scissorRectDirty_)
+    {
+        D3D11_RECT d3dRect;
+        d3dRect.left = scissorRect_.left_;
+        d3dRect.top = scissorRect_.top_;
+        d3dRect.right = scissorRect_.right_;
+        d3dRect.bottom = scissorRect_.bottom_;
+        impl_->deviceContext_->RSSetScissorRects(1, &d3dRect);
+        scissorRectDirty_ = false;
+    }
+
+    for (unsigned i = 0; i < dirtyConstantBuffers_.Size(); ++i)
+        dirtyConstantBuffers_[i]->Apply();
+    dirtyConstantBuffers_.Clear();
+}
+
+void Graphics::SetTextureUnitMappings()
+{
+    textureUnits_["DiffMap"] = TU_DIFFUSE;
+    textureUnits_["DiffCubeMap"] = TU_DIFFUSE;
+    textureUnits_["NormalMap"] = TU_NORMAL;
+    textureUnits_["SpecMap"] = TU_SPECULAR;
+    textureUnits_["EmissiveMap"] = TU_EMISSIVE;
+    textureUnits_["EnvMap"] = TU_ENVIRONMENT;
+    textureUnits_["EnvCubeMap"] = TU_ENVIRONMENT;
+    textureUnits_["LightRampMap"] = TU_LIGHTRAMP;
+    textureUnits_["LightSpotMap"] = TU_LIGHTSHAPE;
+    textureUnits_["LightCubeMap"]  = TU_LIGHTSHAPE;
+    textureUnits_["ShadowMap"] = TU_SHADOWMAP;
+    textureUnits_["FaceSelectCubeMap"] = TU_FACESELECT;
+    textureUnits_["IndirectionCubeMap"] = TU_INDIRECTION;
+    textureUnits_["VolumeMap"] = TU_VOLUMEMAP;
+    textureUnits_["ZoneCubeMap"] = TU_ZONE;
+    textureUnits_["ZoneVolumeMap"] = TU_ZONE;
+}
+
+void RegisterGraphicsLibrary(Context* context)
+{
+    Animation::RegisterObject(context);
+    Material::RegisterObject(context);
+    Model::RegisterObject(context);
+    Shader::RegisterObject(context);
+    Technique::RegisterObject(context);
+    Texture2D::RegisterObject(context);
+    Texture3D::RegisterObject(context);
+    TextureCube::RegisterObject(context);
+    Camera::RegisterObject(context);
+    Drawable::RegisterObject(context);
+    Light::RegisterObject(context);
+    StaticModel::RegisterObject(context);
+    StaticModelGroup::RegisterObject(context);
+    Skybox::RegisterObject(context);
+    AnimatedModel::RegisterObject(context);
+    AnimationController::RegisterObject(context);
+    BillboardSet::RegisterObject(context);
+    ParticleEffect::RegisterObject(context);
+    ParticleEmitter::RegisterObject(context);
+    CustomGeometry::RegisterObject(context);
+    DecalSet::RegisterObject(context);
+    Terrain::RegisterObject(context);
+    TerrainPatch::RegisterObject(context);
+    DebugRenderer::RegisterObject(context);
+    Octree::RegisterObject(context);
+    Zone::RegisterObject(context);
+}
+
+}

+ 638 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Graphics.h

@@ -0,0 +1,638 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/ArrayPtr.h"
+#include "../../Math/Color.h"
+#include "../../Container/HashSet.h"
+#include "../../Resource/Image.h"
+#include "../../Core/Mutex.h"
+#include "../../Core/Object.h"
+#include "../../Math/Plane.h"
+#include "../../Math/Rect.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Graphics/ShaderVariation.h"
+
+namespace Urho3D
+{
+
+class ConstantBuffer;
+class File;
+class Image;
+class IndexBuffer;
+class GPUObject;
+class GraphicsImpl;
+class RenderSurface;
+class Shader;
+class ShaderPrecache;
+class ShaderProgram;
+class ShaderVariation;
+class Texture;
+class Texture2D;
+class TextureCube;
+class Vector3;
+class Vector4;
+class VertexBuffer;
+class VertexDeclaration;
+
+struct ShaderParameter;
+
+/// CPU-side scratch buffer for vertex data updates.
+struct ScratchBuffer
+{
+    ScratchBuffer() :
+        size_(0),
+        reserved_(false)
+    {
+    }
+    
+    /// Buffer data.
+    SharedArrayPtr<unsigned char> data_;
+    /// Data size.
+    unsigned size_;
+    /// Reserved flag.
+    bool reserved_;
+};
+
+typedef HashMap<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
+
+/// %Graphics subsystem. Manages the application window, rendering state and GPU resources.
+class URHO3D_API Graphics : public Object
+{
+    OBJECT(Graphics);
+    
+public:
+    /// Construct.
+    Graphics(Context* context);
+    /// Destruct. Release the Direct3D11 device and close the window.
+    virtual ~Graphics();
+    
+    /// Set external window handle. Only effective before setting the initial screen mode.
+    void SetExternalWindow(void* window);
+    /// Set window title.
+    void SetWindowTitle(const String& windowTitle);
+    /// Set window icon.
+    void SetWindowIcon(Image* windowIcon);
+    /// Set window position. Sets initial position if window is not created yet.
+    void SetWindowPosition(const IntVector2& position);
+    /// Set window position. Sets initial position if window is not created yet.
+    void SetWindowPosition(int x, int y);
+    /// Set screen mode. Return true if successful.
+    bool SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable, bool vsync, bool tripleBuffer, int multiSample);
+    /// Set screen resolution only. Return true if successful.
+    bool SetMode(int width, int height);
+    /// Set whether the main window uses sRGB conversion on write.
+    void SetSRGB(bool enable);
+    /// Set whether to flush the GPU command buffer to prevent multiple frames being queued and uneven frame timesteps. Default off, may decrease performance if enabled.
+    void SetFlushGPU(bool enable);
+    /// Set allowed screen orientations as a space-separated list of "LandscapeLeft", "LandscapeRight", "Portrait" and "PortraitUpsideDown". Affects currently only iOS platform.
+    void SetOrientations(const String& orientations);
+    /// Toggle between full screen and windowed mode. Return true if successful.
+    bool ToggleFullscreen();
+    /// Close the window.
+    void Close();
+    /// Take a screenshot. Return true if successful.
+    bool TakeScreenShot(Image& destImage);
+    /// Begin frame rendering. Return true if device available and can render.
+    bool BeginFrame();
+    /// End frame rendering and swap buffers.
+    void EndFrame();
+    /// Clear any or all of rendertarget, depth buffer and stencil buffer.
+    void Clear(unsigned flags, const Color& color = Color(0.0f, 0.0f, 0.0f, 0.0f), float depth = 1.0f, unsigned stencil = 0);
+    /// Resolve multisampled backbuffer to a texture rendertarget. The texture's size should match the viewport size.
+    bool ResolveToTexture(Texture2D* destination, const IntRect& viewport);
+    /// Draw non-indexed geometry.
+    void Draw(PrimitiveType type, unsigned vertexStart, unsigned vertexCount);
+    /// Draw indexed geometry.
+    void Draw(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount);
+    /// Draw indexed, instanced geometry. An instancing vertex buffer must be set.
+    void DrawInstanced(PrimitiveType type, unsigned indexStart, unsigned indexCount, unsigned minVertex, unsigned vertexCount, unsigned instanceCount);
+    /// Set vertex buffer.
+    void SetVertexBuffer(VertexBuffer* buffer);
+    /// Set multiple vertex buffers.
+    bool SetVertexBuffers(const PODVector<VertexBuffer*>& buffers, const PODVector<unsigned>& elementMasks, unsigned instanceOffset = 0);
+    /// Set multiple vertex buffers.
+    bool SetVertexBuffers(const Vector<SharedPtr<VertexBuffer> >& buffers, const PODVector<unsigned>& elementMasks, unsigned instanceOffset = 0);
+    /// Set index buffer.
+    void SetIndexBuffer(IndexBuffer* buffer);
+    /// Set shaders.
+    void SetShaders(ShaderVariation* vs, ShaderVariation* ps);
+    /// Set shader float constants.
+    void SetShaderParameter(StringHash param, const float* data, unsigned count);
+    /// Set shader float constant.
+    void SetShaderParameter(StringHash param, float value);
+    /// Set shader boolean constant.
+    void SetShaderParameter(StringHash param, bool value);
+    /// Set shader color constant.
+    void SetShaderParameter(StringHash param, const Color& color);
+    /// Set shader 2D vector constant.
+    void SetShaderParameter(StringHash param, const Vector2& vector);
+    /// Set shader 3x3 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix3& matrix);
+    /// Set shader 3D vector constant.
+    void SetShaderParameter(StringHash param, const Vector3& vector);
+    /// Set shader 4x4 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix4& matrix);
+    /// Set shader 4D vector constant.
+    void SetShaderParameter(StringHash param, const Vector4& vector);
+    /// Set shader 3x4 matrix constant.
+    void SetShaderParameter(StringHash param, const Matrix3x4& matrix);
+    /// Set shader constant from a variant. Supported variant types: bool, float, vector2, vector3, vector4, color.
+    void SetShaderParameter(StringHash param, const Variant& value);
+    /// Check whether a shader parameter group needs update. Does not actually check whether parameters exist in the shaders.
+    bool NeedParameterUpdate(ShaderParameterGroup group, const void* source);
+    /// Check whether a shader parameter exists on the currently set shaders.
+    bool HasShaderParameter(StringHash param);
+    /// Check whether the current pixel shader uses a texture unit.
+    bool HasTextureUnit(TextureUnit unit);
+    /// Clear remembered shader parameter source group.
+    void ClearParameterSource(ShaderParameterGroup group);
+    /// Clear remembered shader parameter sources.
+    void ClearParameterSources();
+    /// Clear remembered transform shader parameter sources.
+    void ClearTransformSources();
+    /// Set texture.
+    void SetTexture(unsigned index, Texture* texture);
+    /// Set default texture filtering mode.
+    void SetDefaultTextureFilterMode(TextureFilterMode mode);
+    /// Set texture anisotropy.
+    void SetTextureAnisotropy(unsigned level);
+    /// Dirty texture parameters of all textures (when global settings change.)
+    void SetTextureParametersDirty();
+    /// Reset all rendertargets, depth-stencil surface and viewport.
+    void ResetRenderTargets();
+    /// Reset specific rendertarget.
+    void ResetRenderTarget(unsigned index);
+    /// Reset depth-stencil surface.
+    void ResetDepthStencil();
+    /// Set rendertarget.
+    void SetRenderTarget(unsigned index, RenderSurface* renderTarget);
+    /// Set rendertarget.
+    void SetRenderTarget(unsigned index, Texture2D* texture);
+    /// Set depth-stencil surface.
+    void SetDepthStencil(RenderSurface* depthStencil);
+    /// Set depth-stencil surface.
+    void SetDepthStencil(Texture2D* texture);
+    /// Set viewport.
+    void SetViewport(const IntRect& rect);
+    /// Set blending mode.
+    void SetBlendMode(BlendMode mode);
+    /// Set color write on/off.
+    void SetColorWrite(bool enable);
+    /// Set hardware culling mode.
+    void SetCullMode(CullMode mode);
+    /// Set depth bias.
+    void SetDepthBias(float constantBias, float slopeScaledBias);
+    /// Set depth compare.
+    void SetDepthTest(CompareMode mode);
+    /// Set depth write on/off.
+    void SetDepthWrite(bool enable);
+    /// Set polygon fill mode.
+    void SetFillMode(FillMode mode);
+    /// Set scissor test.
+    void SetScissorTest(bool enable, const Rect& rect = Rect::FULL, bool borderInclusive = true);
+    /// Set scissor test.
+    void SetScissorTest(bool enable, const IntRect& rect);
+    /// Set stencil test.
+    void SetStencilTest(bool enable, CompareMode mode = CMP_ALWAYS, StencilOp pass = OP_KEEP, StencilOp fail = OP_KEEP, StencilOp zFail = OP_KEEP, unsigned stencilRef = 0, unsigned compareMask = M_MAX_UNSIGNED, unsigned writeMask = M_MAX_UNSIGNED);
+    /// Set a custom clipping plane. The plane is specified in world space, but is dependent on the view and projection matrices.
+    void SetClipPlane(bool enable, const Plane& clipPlane = Plane::UP, const Matrix3x4& view = Matrix3x4::IDENTITY, const Matrix4& projection = Matrix4::IDENTITY);
+    /// Begin dumping shader variation names to an XML file for precaching.
+    void BeginDumpShaders(const String& fileName);
+    /// End dumping shader variations names.
+    void EndDumpShaders();
+    /// Precache shader variations from an XML file generated with BeginDumpShaders().
+    void PrecacheShaders(Deserializer& source);
+    
+    /// Return whether rendering initialized.
+    bool IsInitialized() const;
+    /// Return graphics implementation, which holds the actual API-specific resources.
+    GraphicsImpl* GetImpl() const { return impl_; }
+    /// Return OS-specific external window handle. Null if not in use.
+    void* GetExternalWindow() const { return externalWindow_; }
+    /// Return window title.
+    const String& GetWindowTitle() const { return windowTitle_; }
+    /// Return graphics API name.
+    const String& GetApiName() const { return apiName_; }
+    /// Return window position.
+    IntVector2 GetWindowPosition() const;
+    /// Return window width.
+    int GetWidth() const { return width_; }
+    /// Return window height.
+    int GetHeight() const { return height_; }
+    /// Return multisample mode (1 = no multisampling.)
+    int GetMultiSample() const { return multiSample_; }
+    /// Return whether window is fullscreen.
+    bool GetFullscreen() const { return fullscreen_; }
+    /// Return whether window is resizable.
+    bool GetResizable() const { return resizable_; }
+    /// Return whether window is borderless.
+    bool GetBorderless() const { return borderless_; }
+    /// Return whether vertical sync is on.
+    bool GetVSync() const { return vsync_; }
+    /// Return whether triple buffering is enabled.
+    bool GetTripleBuffer() const { return tripleBuffer_; }
+    /// Return whether the main window is using sRGB conversion on write.
+    bool GetSRGB() const { return sRGB_; }
+    /// Return whether the GPU command buffer is flushed each frame.
+    bool GetFlushGPU() const { return flushGPU_; }
+    /// Return allowed screen orientations.
+    const String& GetOrientations() const { return orientations_; }
+    /// Return whether Direct3D device is lost, and can not yet render. Always false on D3D11.
+    bool IsDeviceLost() const { return false; }
+    /// Return number of primitives drawn this frame.
+    unsigned GetNumPrimitives() const { return numPrimitives_; }
+    /// Return number of batches drawn this frame.
+    unsigned GetNumBatches() const { return numBatches_; }
+    /// Return dummy color texture format for shadow maps. Is "NULL" (consume no video memory) if supported.
+    unsigned GetDummyColorFormat() const { return dummyColorFormat_; }
+    /// Return shadow map depth texture format, or 0 if not supported.
+    unsigned GetShadowMapFormat() const { return shadowMapFormat_; }
+    /// Return 24-bit shadow map depth texture format, or 0 if not supported.
+    unsigned GetHiresShadowMapFormat() const { return hiresShadowMapFormat_; }
+    /// Return whether hardware instancing is supported..
+    bool GetInstancingSupport() const { return instancingSupport_; }
+    /// Return whether light pre-pass rendering is supported.
+    bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
+    /// Return whether deferred rendering is supported.
+    bool GetDeferredSupport() const { return deferredSupport_; }
+    /// Return whether shadow map depth compare is done in hardware.
+    bool GetHardwareShadowSupport() const { return hardwareShadowSupport_; }
+    /// Return whether a readable hardware depth format is available.
+    bool GetReadableDepthSupport() const { return GetReadableDepthFormat() != 0; }
+    /// Return whether sRGB conversion on texture sampling is supported.
+    bool GetSRGBSupport() const { return sRGBSupport_; }
+    /// Return whether sRGB conversion on rendertarget writing is supported.
+    bool GetSRGBWriteSupport() const { return sRGBWriteSupport_; }
+    /// Return supported fullscreen resolutions.
+    PODVector<IntVector2> GetResolutions() const;
+    /// Return supported multisampling levels.
+    PODVector<int> GetMultiSampleLevels() const;
+    /// Return the desktop resolution.
+    IntVector2 GetDesktopResolution() const;
+    /// Return hardware format for a compressed image format, or 0 if unsupported.
+    unsigned GetFormat(CompressedFormat format) const;
+    /// Return a shader variation by name and defines.
+    ShaderVariation* GetShader(ShaderType type, const String& name, const String& defines = String::EMPTY) const;
+    /// Return a shader variation by name and defines.
+    ShaderVariation* GetShader(ShaderType type, const char* name, const char* defines) const;
+    /// Return current vertex buffer by index.
+    VertexBuffer* GetVertexBuffer(unsigned index) const;
+    /// Return current index buffer.
+    IndexBuffer* GetIndexBuffer() const { return indexBuffer_; }
+    /// Return current vertex shader.
+    ShaderVariation* GetVertexShader() const { return vertexShader_; }
+    /// Return current pixel shader.
+    ShaderVariation* GetPixelShader() const { return pixelShader_; }
+    /// Return texture unit index by name.
+    TextureUnit GetTextureUnit(const String& name);
+    /// Return texture unit name by index.
+    const String& GetTextureUnitName(TextureUnit unit);
+    /// Return current texture by texture unit index.
+    Texture* GetTexture(unsigned index) const;
+    /// Return default texture filtering mode.
+    TextureFilterMode GetDefaultTextureFilterMode() const { return defaultTextureFilterMode_; }
+    /// Return current rendertarget by index.
+    RenderSurface* GetRenderTarget(unsigned index) const;
+    /// Return current depth-stencil surface.
+    RenderSurface* GetDepthStencil() const { return depthStencil_; }
+    /// Return the viewport coordinates.
+    IntRect GetViewport() const { return viewport_; }
+    /// Return texture anisotropy.
+    unsigned GetTextureAnisotropy() const { return textureAnisotropy_; }
+    /// Return blending mode.
+    BlendMode GetBlendMode() const { return blendMode_; }
+    /// Return whether color write is enabled.
+    bool GetColorWrite() const { return colorWrite_; }
+    /// Return hardware culling mode.
+    CullMode GetCullMode() const { return cullMode_; }
+    /// Return depth constant bias.
+    float GetDepthConstantBias() const { return constantDepthBias_; }
+    /// Return depth slope scaled bias.
+    float GetDepthSlopeScaledBias() const { return slopeScaledDepthBias_; }
+    /// Return depth compare mode.
+    CompareMode GetDepthTest() const { return depthTestMode_; }
+    /// Return whether depth write is enabled.
+    bool GetDepthWrite() const { return depthWrite_; }
+    /// Return polygon fill mode.
+    FillMode GetFillMode() const { return fillMode_; }
+    /// Return whether stencil test is enabled.
+    bool GetStencilTest() const { return stencilTest_; }
+    /// Return whether scissor test is enabled.
+    bool GetScissorTest() const { return scissorTest_; }
+    /// Return scissor rectangle coordinates.
+    const IntRect& GetScissorRect() const { return scissorRect_; }
+    /// Return stencil compare mode.
+    CompareMode GetStencilTestMode() const { return stencilTestMode_; }
+    /// Return stencil operation to do if stencil test passes.
+    StencilOp GetStencilPass() const { return stencilPass_; }
+    /// Return stencil operation to do if stencil test fails.
+    StencilOp GetStencilFail() const { return stencilFail_; }
+    /// Return stencil operation to do if depth compare fails.
+    StencilOp GetStencilZFail() const { return stencilZFail_; }
+    /// Return stencil reference value.
+    unsigned GetStencilRef() const { return stencilRef_; }
+    /// Return stencil compare bitmask.
+    unsigned GetStencilCompareMask() const { return stencilCompareMask_; }
+    /// Return stencil write bitmask.
+    unsigned GetStencilWriteMask() const { return stencilWriteMask_; }
+    /// Return whether a custom clipping plane is in use.
+    bool GetUseClipPlane() const { return useClipPlane_; }
+    /// Return rendertarget width and height.
+    IntVector2 GetRenderTargetDimensions() const;
+    
+    /// Window was resized through user interaction. Called by Input subsystem.
+    void WindowResized();
+    /// Window was moved through user interaction. Called by Input subsystem.
+    void WindowMoved();
+    /// Maximize the Window.
+    void Maximize();
+    /// Minimize the Window.
+    void Minimize();
+    /// Add a GPU object to keep track of. Called by GPUObject.
+    void AddGPUObject(GPUObject* object);
+    /// Remove a GPU object. Called by GPUObject.
+    void RemoveGPUObject(GPUObject* object);
+    /// Reserve a CPU-side scratch buffer.
+    void* ReserveScratchBuffer(unsigned size);
+    /// Free a CPU-side scratch buffer.
+    void FreeScratchBuffer(void* buffer);
+    /// Clean up too large scratch buffers.
+    void CleanupScratchBuffers();
+    /// Clean up shader parameters when a shader variation is released or destroyed.
+    void CleanUpShaderPrograms(ShaderVariation* variation);
+    /// Get or create a constant buffer. Will be shared between shaders if possible.
+    ConstantBuffer* GetOrCreateConstantBuffer(ShaderType type, unsigned index, unsigned size);
+
+    /// Return the API-specific alpha texture format.
+    static unsigned GetAlphaFormat();
+    /// Return the API-specific luminance texture format.
+    static unsigned GetLuminanceFormat();
+    /// Return the API-specific luminance alpha texture format.
+    static unsigned GetLuminanceAlphaFormat();
+    /// Return the API-specific RGB texture format.
+    static unsigned GetRGBFormat();
+    /// Return the API-specific RGBA texture format.
+    static unsigned GetRGBAFormat();
+    /// Return the API-specific RGBA 16-bit texture format.
+    static unsigned GetRGBA16Format();
+    /// Return the API-specific RGBA 16-bit float texture format.
+    static unsigned GetRGBAFloat16Format();
+    /// Return the API-specific RGBA 32-bit float texture format.
+    static unsigned GetRGBAFloat32Format();
+    /// Return the API-specific RG 16-bit texture format.
+    static unsigned GetRG16Format();
+    /// Return the API-specific RG 16-bit float texture format.
+    static unsigned GetRGFloat16Format();
+    /// Return the API-specific RG 32-bit float texture format.
+    static unsigned GetRGFloat32Format();
+    /// Return the API-specific single channel 16-bit float texture format.
+    static unsigned GetFloat16Format();
+    /// Return the API-specific single channel 32-bit float texture format.
+    static unsigned GetFloat32Format();
+    /// Return the API-specific linear depth texture format.
+    static unsigned GetLinearDepthFormat();
+    /// Return the API-specific hardware depth-stencil texture format.
+    static unsigned GetDepthStencilFormat();
+    /// Return the API-specific readable hardware depth format, or 0 if not supported.
+    static unsigned GetReadableDepthFormat();
+    /// Return the API-specific texture format from a textual description, for example "rgb".
+    static unsigned GetFormat(const String& formatName);
+    /// Return UV offset required for pixel perfect rendering.
+    static const Vector2& GetPixelUVOffset() { return pixelUVOffset; }
+    /// Return maximum number of supported bones for skinning.
+    static unsigned GetMaxBones() { return 128; }
+
+private:
+    /// Create the application window.
+    bool OpenWindow(int width, int height, bool resizable, bool borderless);
+    /// Create the application window icon.
+    void CreateWindowIcon();
+    /// Adjust the window for new resolution and fullscreen mode.
+    void AdjustWindow(int& newWidth, int& newHeight, bool& newFullscreen, bool& newBorderless);
+    /// Create the D3D11 device and swap chain. Requires an open window. Can also be called again to recreate swap chain. Return true on success.
+    bool CreateDevice(int width, int height, int multiSample);
+    /// Update swap chain state for a new mode and create views for the backbuffer & default depth buffer. Return true on success.
+    bool UpdateSwapChain(int width, int height);
+    /// Check supported rendering features.
+    void CheckFeatureSupport();
+    /// Reset cached rendering state.
+    void ResetCachedState();
+    /// Initialize texture unit mappings.
+    void SetTextureUnitMappings();
+    /// Process dirtied state before draw.
+    void PrepareDraw();
+    
+    /// Mutex for accessing the GPU objects vector from several threads.
+    Mutex gpuObjectMutex_;
+    /// Implementation.
+    GraphicsImpl* impl_;
+    /// Window title.
+    String windowTitle_;
+    /// Window Icon File Name
+    Image* windowIcon_;
+    /// External window, null if not in use (default.)
+    void* externalWindow_;
+    /// Window width.
+    int width_;
+    /// Window height.
+    int height_;
+    /// Window position.
+    IntVector2 position_;
+    /// Multisampling mode.
+    int multiSample_;
+    /// Fullscreen flag.
+    bool fullscreen_;
+    /// Borderless flag.
+    bool borderless_;
+    /// Resizable flag.
+    bool resizable_;
+    /// Vertical sync flag.
+    bool vsync_;
+    /// Triple buffering flag.
+    bool tripleBuffer_;
+    /// Flush GPU command buffer flag.
+    bool flushGPU_;
+    /// sRGB conversion on write flag for the main window.
+    bool sRGB_;
+    /// Light pre-pass rendering support flag.
+    bool lightPrepassSupport_;
+    /// Deferred rendering support flag.
+    bool deferredSupport_;
+    /// Hardware shadow map depth compare support flag.
+    bool hardwareShadowSupport_;
+    /// Instancing support flag.
+    bool instancingSupport_;
+    /// sRGB conversion on read support flag.
+    bool sRGBSupport_;
+    /// sRGB conversion on write support flag.
+    bool sRGBWriteSupport_;
+    /// Number of primitives this frame.
+    unsigned numPrimitives_;
+    /// Number of batches this frame.
+    unsigned numBatches_;
+    /// Largest scratch buffer request this frame.
+    unsigned maxScratchBufferRequest_;
+    /// GPU objects.
+    PODVector<GPUObject*> gpuObjects_;
+    /// Scratch buffers.
+    Vector<ScratchBuffer> scratchBuffers_;
+    /// Shadow map dummy color texture format.
+    unsigned dummyColorFormat_;
+    /// Shadow map depth texture format.
+    unsigned shadowMapFormat_;
+    /// Shadow map 24-bit depth texture format.
+    unsigned hiresShadowMapFormat_;
+    /// Vertex buffers in use.
+    VertexBuffer* vertexBuffers_[MAX_VERTEX_STREAMS];
+    /// Element masks by vertex buffer.
+    unsigned elementMasks_[MAX_VERTEX_STREAMS];
+    /// Index buffer in use.
+    IndexBuffer* indexBuffer_;
+    /// Current vertex declaration hash.
+    unsigned long long vertexDeclarationHash_;
+    /// Current primitive type.
+    unsigned primitiveType_;
+    /// Vertex shader in use.
+    ShaderVariation* vertexShader_;
+    /// Pixel shader in use.
+    ShaderVariation* pixelShader_;
+    /// Textures in use.
+    Texture* textures_[MAX_TEXTURE_UNITS];
+    /// Texture unit mappings.
+    HashMap<String, TextureUnit> textureUnits_;
+    /// Rendertargets in use.
+    RenderSurface* renderTargets_[MAX_RENDERTARGETS];
+    /// Depth-stencil surface in use.
+    RenderSurface* depthStencil_;
+    /// Viewport coordinates.
+    IntRect viewport_;
+    /// Texture anisotropy level.
+    unsigned textureAnisotropy_;
+    /// Blending mode.
+    BlendMode blendMode_;
+    /// Color write enable.
+    bool colorWrite_;
+    /// Hardware culling mode.
+    CullMode cullMode_;
+    /// Depth constant bias.
+    float constantDepthBias_;
+    /// Depth slope scaled bias.
+    float slopeScaledDepthBias_;
+    /// Depth compare mode.
+    CompareMode depthTestMode_;
+    /// Depth write enable flag.
+    bool depthWrite_;
+    /// Polygon fill mode.
+    FillMode fillMode_;
+    /// Scissor test rectangle.
+    IntRect scissorRect_;
+    /// Scissor test enable flag.
+    bool scissorTest_;
+    /// Stencil test compare mode.
+    CompareMode stencilTestMode_;
+    /// Stencil operation on pass.
+    StencilOp stencilPass_;
+    /// Stencil operation on fail.
+    StencilOp stencilFail_;
+    /// Stencil operation on depth fail.
+    StencilOp stencilZFail_;
+    /// Stencil test reference value.
+    unsigned stencilRef_;
+    /// Stencil compare bitmask.
+    unsigned stencilCompareMask_;
+    /// Stencil write bitmask.
+    unsigned stencilWriteMask_;
+    /// Current custom clip plane in post-projection space.
+    Vector4 clipPlane_;
+    /// Stencil test enable flag.
+    bool stencilTest_;
+    /// Custom clip plane enable flag.
+    bool useClipPlane_;
+    /// Rendertargets dirty flag.
+    bool renderTargetsDirty_;
+    /// Textures dirty flag.
+    bool texturesDirty_;
+    /// Vertex declaration dirty flag.
+    bool vertexDeclarationDirty_;
+    /// Blend state dirty flag.
+    bool blendStateDirty_;
+    /// Depth state dirty flag.
+    bool depthStateDirty_;
+    /// Rasterizer state dirty flag.
+    bool rasterizerStateDirty_;
+    /// Scissor rect dirty flag.
+    bool scissorRectDirty_;
+    /// Stencil ref dirty flag.
+    bool stencilRefDirty_;
+    /// Hash of current blend state.
+    unsigned blendStateHash_;
+    /// Hash of current depth state.
+    unsigned depthStateHash_;
+    /// Hash of current rasterizer state.
+    unsigned rasterizerStateHash_;
+    /// First dirtied texture unit.
+    unsigned firstDirtyTexture_;
+    /// Last dirtied texture unit.
+    unsigned lastDirtyTexture_;
+    /// First dirtied vertex buffer.
+    unsigned firstDirtyVB_;
+    /// Last dirtied vertex buffer.
+    unsigned lastDirtyVB_;
+    /// Default texture filtering mode.
+    TextureFilterMode defaultTextureFilterMode_;
+    /// Vertex declarations.
+    HashMap<unsigned long long, SharedPtr<VertexDeclaration> > vertexDeclarations_;
+    /// Constant buffers.
+    HashMap<unsigned, SharedPtr<ConstantBuffer> > constantBuffers_;
+    /// Currently dirty constant buffers.
+    PODVector<ConstantBuffer*> dirtyConstantBuffers_;
+    /// Shader programs.
+    ShaderProgramMap shaderPrograms_;
+    /// Shader program in use.
+    ShaderProgram* shaderProgram_;
+    /// Remembered shader parameter sources.
+    const void* shaderParameterSources_[MAX_SHADER_PARAMETER_GROUPS];
+    /// Base directory for shaders.
+    String shaderPath_;
+    /// File extension for shaders.
+    String shaderExtension_;
+    /// Last used shader in shader variation query.
+    mutable WeakPtr<Shader> lastShader_;
+    /// Last used shader name in shader variation query.
+    mutable String lastShaderName_;
+    /// Shader precache utility.
+    SharedPtr<ShaderPrecache> shaderPrecache_;
+    /// Allowed screen orientations.
+    String orientations_;
+    /// Graphics API name.
+    String apiName_;
+
+    /// Pixel perfect UV offset.
+    static const Vector2 pixelUVOffset;
+};
+
+/// Register Graphics library objects.
+void URHO3D_API RegisterGraphicsLibrary(Context* context);
+
+}

+ 65 - 0
Source/Atomic/Graphics/Direct3D11/D3D11GraphicsImpl.cpp

@@ -0,0 +1,65 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+GraphicsImpl::GraphicsImpl() :
+    window_(0),
+    device_(0),
+    deviceContext_(0),
+    swapChain_(0),
+    defaultRenderTargetView_(0),
+    defaultDepthTexture_(0),
+    defaultDepthStencilView_(0),
+    depthStencilView_(0)
+{
+    for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        renderTargetViews_[i] = 0;
+
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        shaderResourceViews_[i] = 0;
+        samplers_[i] = 0;
+    }
+
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        vertexBuffers_[i] = 0;
+        vertexSizes_[i] = 0;
+        vertexOffsets_[i] = 0;
+    }
+
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+    {
+        constantBuffers_[VS][i] = 0;
+        constantBuffers_[PS][i] = 0;
+    }
+}
+
+}

+ 92 - 0
Source/Atomic/Graphics/Direct3D11/D3D11GraphicsImpl.h

@@ -0,0 +1,92 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Math/Color.h"
+#include "../../Graphics/GraphicsDefs.h"
+
+#include <d3d11.h>
+#include <dxgi.h>
+#include <SDL/SDL.h>
+
+namespace Urho3D
+{
+
+/// %Graphics implementation. Holds API-specific objects.
+class URHO3D_API GraphicsImpl
+{
+    friend class Graphics;
+    
+public:
+    /// Construct.
+    GraphicsImpl();
+    
+    /// Return Direct3D device.
+    ID3D11Device* GetDevice() const { return device_; }
+    /// Return Direct3D immediate device context.
+    ID3D11DeviceContext* GetDeviceContext() const { return deviceContext_; }
+    /// Return swapchain.
+    IDXGISwapChain* GetSwapChain() const { return swapChain_; }
+    /// Return window.
+    SDL_Window* GetWindow() const { return window_; }
+    
+private:
+    /// SDL window.
+    SDL_Window* window_;
+    /// Graphics device.
+    ID3D11Device* device_;
+    /// Immediate device context.
+    ID3D11DeviceContext* deviceContext_;
+    /// Swap chain.
+    IDXGISwapChain* swapChain_;
+    /// Default (backbuffer) rendertarget view.
+    ID3D11RenderTargetView* defaultRenderTargetView_;
+    /// Default depth-stencil texture.
+    ID3D11Texture2D* defaultDepthTexture_;
+    /// Default depth-stencil view.
+    ID3D11DepthStencilView* defaultDepthStencilView_;
+    /// Current color rendertarget views.
+    ID3D11RenderTargetView* renderTargetViews_[MAX_RENDERTARGETS];
+    /// Current depth-stencil view.
+    ID3D11DepthStencilView* depthStencilView_;
+    /// Created blend state objects.
+    HashMap<unsigned, ID3D11BlendState*> blendStates_;
+    /// Created depth state objects.
+    HashMap<unsigned, ID3D11DepthStencilState*> depthStates_;
+    /// Created rasterizer state objects.
+    HashMap<unsigned, ID3D11RasterizerState*> rasterizerStates_;
+    /// Bound shader resource views.
+    ID3D11ShaderResourceView* shaderResourceViews_[MAX_TEXTURE_UNITS];
+    /// Bound sampler state objects.
+    ID3D11SamplerState* samplers_[MAX_TEXTURE_UNITS];
+    /// Bound vertex buffers.
+    ID3D11Buffer* vertexBuffers_[MAX_VERTEX_STREAMS];
+    /// Bound constant buffers.
+    ID3D11Buffer* constantBuffers_[2][MAX_SHADER_PARAMETER_GROUPS];
+    /// Vertex sizes per buffer.
+    unsigned vertexSizes_[MAX_VERTEX_STREAMS];
+    /// Vertex stream offsets per buffer.
+    unsigned vertexOffsets_[MAX_VERTEX_STREAMS];
+};
+
+}

+ 391 - 0
Source/Atomic/Graphics/Direct3D11/D3D11IndexBuffer.cpp

@@ -0,0 +1,391 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/Context.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/IndexBuffer.h"
+#include "../../IO/Log.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+IndexBuffer::IndexBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>()),
+    indexCount_(0),
+    indexSize_(0),
+    lockState_(LOCK_NONE),
+    lockStart_(0),
+    lockCount_(0),
+    lockScratchData_(0),
+    dynamic_(false),
+    shadowed_(false)
+{
+    // Force shadowing mode if graphics subsystem does not exist
+    if (!graphics_)
+        shadowed_ = true;
+}
+
+IndexBuffer::~IndexBuffer()
+{
+    Release();
+}
+
+void IndexBuffer::Release()
+{
+    Unlock();
+    
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        if (graphics_->GetIndexBuffer() == this)
+            graphics_->SetIndexBuffer(0);
+        
+        ((ID3D11Buffer*)object_)->Release();
+        object_ = 0;
+    }
+}
+
+void IndexBuffer::SetShadowed(bool enable)
+{
+    // If no graphics subsystem, can not disable shadowing
+    if (!graphics_)
+        enable = true;
+    
+    if (enable != shadowed_)
+    {
+        if (enable && indexCount_ && indexSize_)
+            shadowData_ = new unsigned char[indexCount_ * indexSize_];
+        else
+            shadowData_.Reset();
+        
+        shadowed_ = enable;
+    }
+}
+
+bool IndexBuffer::SetSize(unsigned indexCount, bool largeIndices, bool dynamic)
+{
+    Unlock();
+    
+    dynamic_ = dynamic;
+    indexCount_ = indexCount;
+    indexSize_ = largeIndices ? sizeof(unsigned) : sizeof(unsigned short);
+    
+    if (shadowed_ && indexCount_ && indexSize_)
+        shadowData_ = new unsigned char[indexCount_ * indexSize_];
+    else
+        shadowData_.Reset();
+    
+    return Create();
+}
+
+bool IndexBuffer::SetData(const void* data)
+{
+    if (!data)
+    {
+        LOGERROR("Null pointer for index buffer data");
+        return false;
+    }
+    
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not set index buffer data");
+        return false;
+    }
+    
+    if (shadowData_ && data != shadowData_.Get())
+        memcpy(shadowData_.Get(), data, indexCount_ * indexSize_);
+    
+    if (object_)
+    {
+        if (dynamic_)
+        {
+            void* hwData = MapBuffer(0, indexCount_, true);
+            if (hwData)
+            {
+                memcpy(hwData, data, indexCount_ * indexSize_);
+                UnmapBuffer();
+            }
+            else
+                return false;
+        }
+        else
+        {
+            D3D11_BOX destBox;
+            destBox.left = 0;
+            destBox.right = indexCount_ * indexSize_;
+            destBox.top = 0;
+            destBox.bottom = 1;
+            destBox.front = 0;
+            destBox.back = 1;
+
+            graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, &destBox, data, 0, 0);
+        }
+    }
+    
+    return true;
+}
+
+bool IndexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
+{
+    if (start == 0 && count == indexCount_)
+        return SetData(data);
+    
+    if (!data)
+    {
+        LOGERROR("Null pointer for index buffer data");
+        return false;
+    }
+    
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not set index buffer data");
+        return false;
+    }
+    
+    if (start + count > indexCount_)
+    {
+        LOGERROR("Illegal range for setting new index buffer data");
+        return false;
+    }
+    
+    if (!count)
+        return true;
+    
+    if (shadowData_ && shadowData_.Get() + start * indexSize_ != data)
+        memcpy(shadowData_.Get() + start * indexSize_, data, count * indexSize_);
+    
+    if (object_)
+    {
+        if (dynamic_)
+        {
+            void* hwData = MapBuffer(start, count, discard);
+            if (hwData)
+            {
+                memcpy(hwData, data, count * indexSize_);
+                UnmapBuffer();
+            }
+            else
+                return false;
+        }
+        else
+        {
+            D3D11_BOX destBox;
+            destBox.left = start * indexSize_;
+            destBox.right = destBox.left + count * indexSize_;
+            destBox.top = 0;
+            destBox.bottom = 1;
+            destBox.front = 0;
+            destBox.back = 1;
+
+            graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, &destBox, data, 0, 0);
+        }
+    }
+
+    return true;
+}
+
+void* IndexBuffer::Lock(unsigned start, unsigned count, bool discard)
+{
+    if (lockState_ != LOCK_NONE)
+    {
+        LOGERROR("Index buffer already locked");
+        return 0;
+    }
+    
+    if (!indexSize_)
+    {
+        LOGERROR("Index size not defined, can not lock index buffer");
+        return 0;
+    }
+    
+    if (start + count > indexCount_)
+    {
+        LOGERROR("Illegal range for locking index buffer");
+        return 0;
+    }
+    
+    if (!count)
+        return 0;
+    
+    lockStart_ = start;
+    lockCount_ = count;
+    
+    // Because shadow data must be kept in sync, can only lock hardware buffer if not shadowed
+    if (object_ && !shadowData_ && dynamic_)
+        return MapBuffer(start, count, discard);
+    else if (shadowData_)
+    {
+        lockState_ = LOCK_SHADOW;
+        return shadowData_.Get() + start * indexSize_;
+    }
+    else if (graphics_)
+    {
+        lockState_ = LOCK_SCRATCH;
+        lockScratchData_ = graphics_->ReserveScratchBuffer(count * indexSize_);
+        return lockScratchData_;
+    }
+    else
+        return 0;
+}
+
+void IndexBuffer::Unlock()
+{
+    switch (lockState_)
+    {
+    case LOCK_HARDWARE:
+        UnmapBuffer();
+        break;
+        
+    case LOCK_SHADOW:
+        SetDataRange(shadowData_.Get() + lockStart_ * indexSize_, lockStart_, lockCount_);
+        lockState_ = LOCK_NONE;
+        break;
+        
+    case LOCK_SCRATCH:
+        SetDataRange(lockScratchData_, lockStart_, lockCount_);
+        if (graphics_)
+            graphics_->FreeScratchBuffer(lockScratchData_);
+        lockScratchData_ = 0;
+        lockState_ = LOCK_NONE;
+        break;
+    }
+}
+
+bool IndexBuffer::GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount)
+{
+    if (!shadowData_)
+    {
+        LOGERROR("Used vertex range can only be queried from an index buffer with shadow data");
+        return false;
+    }
+    
+    if (start + count > indexCount_)
+    {
+        LOGERROR("Illegal index range for querying used vertices");
+        return false;
+    }
+    
+    minVertex = M_MAX_UNSIGNED;
+    unsigned maxVertex = 0;
+    
+    if (indexSize_ == sizeof(unsigned))
+    {
+        unsigned* indices = ((unsigned*)shadowData_.Get()) + start;
+        
+        for (unsigned i = 0; i < count; ++i)
+        {
+            if (indices[i] < minVertex)
+                minVertex = indices[i];
+            if (indices[i] > maxVertex)
+                maxVertex = indices[i];
+        }
+    }
+    else
+    {
+        unsigned short* indices = ((unsigned short*)shadowData_.Get()) + start;
+        
+        for (unsigned i = 0; i < count; ++i)
+        {
+            if (indices[i] < minVertex)
+                minVertex = indices[i];
+            if (indices[i] > maxVertex)
+                maxVertex = indices[i];
+        }
+    }
+    
+    vertexCount = maxVertex - minVertex + 1;
+    return true;
+}
+
+bool IndexBuffer::Create()
+{
+    Release();
+    
+    if (!indexCount_)
+        return true;
+    
+    if (graphics_)
+    {
+        D3D11_BUFFER_DESC bufferDesc;
+        memset(&bufferDesc, 0, sizeof bufferDesc);
+        bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+        bufferDesc.CPUAccessFlags = dynamic_ ? D3D11_CPU_ACCESS_WRITE : 0;
+        bufferDesc.Usage = dynamic_ ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+        bufferDesc.ByteWidth = (unsigned)(indexCount_ * indexSize_);
+
+        graphics_->GetImpl()->GetDevice()->CreateBuffer(&bufferDesc, 0, (ID3D11Buffer**)&object_);
+
+        if (!object_)
+        {
+            LOGERROR("Failed to create index buffer");
+            return false;
+        }
+    }
+    
+    return true;
+}
+
+bool IndexBuffer::UpdateToGPU()
+{
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
+}
+
+void* IndexBuffer::MapBuffer(unsigned start, unsigned count, bool discard)
+{
+    void* hwData = 0;
+    
+    if (object_)
+    {
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Buffer*)object_, 0, discard ? D3D11_MAP_WRITE_DISCARD :
+            D3D11_MAP_WRITE, 0, &mappedData);
+        hwData = mappedData.pData;
+        if (!hwData)
+            LOGERROR("Failed to map index buffer");
+        else
+            lockState_ = LOCK_HARDWARE;
+    }
+    
+    return hwData;
+}
+
+void IndexBuffer::UnmapBuffer()
+{
+    if (object_ && lockState_ == LOCK_HARDWARE)
+    {
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Buffer*)object_, 0);
+        lockState_ = LOCK_NONE;
+    }
+}
+
+}

+ 107 - 0
Source/Atomic/Graphics/Direct3D11/D3D11IndexBuffer.h

@@ -0,0 +1,107 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Core/Object.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+
+namespace Urho3D
+{
+
+/// Hardware index buffer.
+class URHO3D_API IndexBuffer : public Object, public GPUObject
+{
+    OBJECT(IndexBuffer);
+    
+public:
+    /// Construct.
+    IndexBuffer(Context* context);
+    /// Destruct.
+    virtual ~IndexBuffer();
+    
+    /// Release buffer.
+    virtual void Release();
+    
+    /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
+    void SetShadowed(bool enable);
+    /// Set size and vertex elements and dynamic mode. Previous data will be lost.
+    bool SetSize(unsigned indexCount, bool largeIndices, bool dynamic = false);
+    /// Set all data in the buffer.
+    bool SetData(const void* data);
+    /// Set a data range in the buffer. Optionally discard data outside the range.
+    bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
+    /// Lock the buffer for write-only editing. Return data pointer if successful. Optionally discard data outside the range.
+    void* Lock(unsigned start, unsigned count, bool discard = false);
+    /// Unlock the buffer and apply changes to the GPU buffer.
+    void Unlock();
+    
+    /// Return whether CPU memory shadowing is enabled.
+    bool IsShadowed() const { return shadowed_; }
+    /// Return whether is dynamic.
+    bool IsDynamic() const { return dynamic_; }
+    /// Return whether is currently locked.
+    bool IsLocked() const { return lockState_ != LOCK_NONE; }
+    /// Return number of indices.
+    unsigned GetIndexCount() const {return indexCount_; }
+    /// Return index size.
+    unsigned GetIndexSize() const { return indexSize_; }
+    /// Return used vertex range from index range.
+    bool GetUsedVertexRange(unsigned start, unsigned count, unsigned& minVertex, unsigned& vertexCount);
+    /// Return CPU memory shadow data.
+    unsigned char* GetShadowData() const { return shadowData_.Get(); }
+    /// Return shared array pointer to the CPU memory shadow data.
+    SharedArrayPtr<unsigned char> GetShadowDataShared() const { return shadowData_; }
+
+private:
+    /// Create buffer.
+    bool Create();
+    /// Update the shadow data to the GPU buffer.
+    bool UpdateToGPU();
+    /// Map the GPU buffer into CPU memory.
+    void* MapBuffer(unsigned start, unsigned count, bool discard);
+    /// Unmap the GPU buffer.
+    void UnmapBuffer();
+    
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Number of indices.
+    unsigned indexCount_;
+    /// Index size.
+    unsigned indexSize_;
+    /// Buffer locking state.
+    LockState lockState_;
+    /// Lock start vertex.
+    unsigned lockStart_;
+    /// Lock number of vertices.
+    unsigned lockCount_;
+    /// Scratch buffer for fallback locking.
+    void* lockScratchData_;
+    /// Dynamic flag.
+    bool dynamic_;
+    /// Shadowed flag.
+    bool shadowed_;
+};
+
+}

+ 155 - 0
Source/Atomic/Graphics/Direct3D11/D3D11RenderSurface.cpp

@@ -0,0 +1,155 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Camera.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Graphics/RenderSurface.h"
+#include "../../Scene/Scene.h"
+#include "../../Graphics/Texture.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+RenderSurface::RenderSurface(Texture* parentTexture) :
+    parentTexture_(parentTexture),
+    renderTargetView_(0),
+    updateMode_(SURFACE_UPDATEVISIBLE),
+    updateQueued_(false)
+{
+}
+
+RenderSurface::~RenderSurface()
+{
+    Release();
+}
+
+void RenderSurface::SetNumViewports(unsigned num)
+{
+    viewports_.Resize(num);
+}
+
+void RenderSurface::SetViewport(unsigned index, Viewport* viewport)
+{
+    if (index >= viewports_.Size())
+        viewports_.Resize(index + 1);
+    
+    viewports_[index] = viewport;
+}
+
+void RenderSurface::SetUpdateMode(RenderSurfaceUpdateMode mode)
+{
+    updateMode_ = mode;
+}
+
+void RenderSurface::SetLinkedRenderTarget(RenderSurface* renderTarget)
+{
+    if (renderTarget != this)
+        linkedRenderTarget_ = renderTarget;
+}
+
+void RenderSurface::SetLinkedDepthStencil(RenderSurface* depthStencil)
+{
+    if (depthStencil != this)
+        linkedDepthStencil_ = depthStencil;
+}
+
+void RenderSurface::QueueUpdate()
+{
+    if (!updateQueued_)
+    {
+        bool hasValidView = false;
+        
+        // Verify that there is at least 1 non-null viewport, as otherwise Renderer will not accept the surface and the update flag
+        // will be left on
+        for (unsigned i = 0; i < viewports_.Size(); ++i)
+        {
+            if (viewports_[i])
+            {
+                hasValidView = true;
+                break;
+            }
+        }
+        
+        if (hasValidView)
+        {
+            Renderer* renderer = parentTexture_->GetSubsystem<Renderer>();
+            if (renderer)
+                renderer->QueueRenderSurface(this);
+            
+            updateQueued_ = true;
+        }
+    }
+}
+
+void RenderSurface::Release()
+{
+    Graphics* graphics = parentTexture_->GetGraphics();
+    if (!graphics)
+        return;
+    
+    if (renderTargetView_)
+    {
+        for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
+        {
+            if (graphics->GetRenderTarget(i) == this)
+                graphics->ResetRenderTarget(i);
+        }
+        
+        if (graphics->GetDepthStencil() == this)
+            graphics->ResetDepthStencil();
+        
+        ((ID3D11View*)renderTargetView_)->Release();
+        renderTargetView_ = 0;
+    }
+}
+
+int RenderSurface::GetWidth() const
+{
+    return parentTexture_->GetWidth();
+}
+
+int RenderSurface::GetHeight() const
+{
+    return parentTexture_->GetHeight();
+}
+
+TextureUsage RenderSurface::GetUsage() const
+{
+    return parentTexture_->GetUsage();
+}
+
+Viewport* RenderSurface::GetViewport(unsigned index) const
+{
+    return index < viewports_.Size() ? viewports_[index] : (Viewport*)0;
+}
+
+void RenderSurface::WasUpdated()
+{
+    updateQueued_ = false;
+}
+
+}

+ 101 - 0
Source/Atomic/Graphics/Direct3D11/D3D11RenderSurface.h

@@ -0,0 +1,101 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Graphics/Viewport.h"
+
+namespace Urho3D
+{
+
+class Texture;
+
+/// %Color or depth-stencil surface that can be rendered into.
+class URHO3D_API RenderSurface : public RefCounted
+{
+    friend class Texture2D;
+    friend class TextureCube;
+    
+public:
+    /// Construct with parent texture.
+    RenderSurface(Texture* parentTexture);
+    /// Destruct.
+    ~RenderSurface();
+    
+    /// Set number of viewports.
+    void SetNumViewports(unsigned num);
+    /// Set viewport.
+    void SetViewport(unsigned index, Viewport* viewport);
+    /// Set viewport update mode. Default is to update when visible.
+    void SetUpdateMode(RenderSurfaceUpdateMode mode);
+    /// Set linked color rendertarget.
+    void SetLinkedRenderTarget(RenderSurface* renderTarget);
+    /// Set linked depth-stencil surface.
+    void SetLinkedDepthStencil(RenderSurface* depthStencil);
+    /// Queue manual update of the viewport(s).
+    void QueueUpdate();
+    /// Release surface.
+    void Release();
+    
+    /// Return parent texture.
+    Texture* GetParentTexture() const { return parentTexture_; }
+    /// Return Direct3D rendertarget or depth-stencil view.
+    void* GetRenderTargetView() const { return renderTargetView_; }
+    /// Return width.
+    int GetWidth() const;
+    /// Return height.
+    int GetHeight() const;
+    /// Return usage.
+    TextureUsage GetUsage() const;
+    /// Return number of viewports.
+    unsigned GetNumViewports() const { return viewports_.Size(); }
+    /// Return viewport by index.
+    Viewport* GetViewport(unsigned index) const;
+    /// Return viewport update mode.
+    RenderSurfaceUpdateMode GetUpdateMode() const { return updateMode_; }
+    /// Return linked color rendertarget.
+    RenderSurface* GetLinkedRenderTarget() const { return linkedRenderTarget_; }
+    /// Return linked depth-stencil surface.
+    RenderSurface* GetLinkedDepthStencil() const { return linkedDepthStencil_; }
+    
+    /// Clear update flag. Called by Renderer.
+    void WasUpdated();
+    
+private:
+    /// Parent texture.
+    Texture* parentTexture_;
+    /// Direct3D rendertarget or depth-stencil view.
+    void* renderTargetView_;
+    /// Viewports.
+    Vector<SharedPtr<Viewport> > viewports_;
+    /// Linked color buffer.
+    WeakPtr<RenderSurface> linkedRenderTarget_;
+    /// Linked depth buffer.
+    WeakPtr<RenderSurface> linkedDepthStencil_;
+    /// Update mode for viewports.
+    RenderSurfaceUpdateMode updateMode_;
+    /// Update queued flag.
+    bool updateQueued_;
+};
+
+}

+ 88 - 0
Source/Atomic/Graphics/Direct3D11/D3D11ShaderProgram.h

@@ -0,0 +1,88 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/HashMap.h"
+#include "../../Graphics/ConstantBuffer.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/ShaderVariation.h"
+
+namespace Urho3D
+{
+
+/// Combined information for specific vertex and pixel shaders.
+class URHO3D_API ShaderProgram : public RefCounted
+{
+public:
+    /// Construct.
+    ShaderProgram(Graphics* graphics, ShaderVariation* vertexShader, ShaderVariation* pixelShader)
+    {
+        // Create needed constant buffers
+        const unsigned* vsBufferSizes = vertexShader->GetConstantBufferSizes();
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        {
+            if (vsBufferSizes[i])
+                vsConstantBuffers_[i] = graphics->GetOrCreateConstantBuffer(VS, i, vsBufferSizes[i]);
+        }
+
+        const unsigned* psBufferSizes = pixelShader->GetConstantBufferSizes();
+        for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        {
+            if (psBufferSizes[i])
+                psConstantBuffers_[i] = graphics->GetOrCreateConstantBuffer(PS, i, psBufferSizes[i]);
+        }
+
+        // Copy parameters. Add direct links to constant buffers.
+        const HashMap<StringHash, ShaderParameter>& vsParams = vertexShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = vsParams.Begin(); i != vsParams.End(); ++i)
+        {
+            parameters_[i->first_] = i->second_;
+            parameters_[i->first_].bufferPtr_ = vsConstantBuffers_[i->second_.buffer_].Get();
+        }
+
+        const HashMap<StringHash, ShaderParameter>& psParams = pixelShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = psParams.Begin(); i != psParams.End(); ++i)
+        {
+            parameters_[i->first_] = i->second_;
+            parameters_[i->first_].bufferPtr_ = psConstantBuffers_[i->second_.buffer_].Get();
+        }
+
+        // Optimize shader parameter lookup by rehashing to next power of two
+        parameters_.Rehash(NextPowerOfTwo(parameters_.Size()));
+
+    }
+
+    /// Destruct.
+    ~ShaderProgram()
+    {
+    }
+
+    /// Combined parameters from the vertex and pixel shader.
+    HashMap<StringHash, ShaderParameter> parameters_;
+    /// Vertex shader constant buffers.
+    SharedPtr<ConstantBuffer> vsConstantBuffers_[MAX_SHADER_PARAMETER_GROUPS];
+    /// Pixel shader constant buffers.
+    SharedPtr<ConstantBuffer> psConstantBuffers_[MAX_SHADER_PARAMETER_GROUPS];
+};
+
+}

+ 464 - 0
Source/Atomic/Graphics/Direct3D11/D3D11ShaderVariation.cpp

@@ -0,0 +1,464 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../IO/File.h"
+#include "../../IO/FileSystem.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/Shader.h"
+#include "../../Graphics/ShaderVariation.h"
+#include "../../Graphics/VertexBuffer.h"
+
+#include <windows.h>
+#include <d3dcompiler.h>
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+ShaderVariation::ShaderVariation(Shader* owner, ShaderType type) :
+    GPUObject(owner->GetSubsystem<Graphics>()),
+    owner_(owner),
+    type_(type),
+    elementMask_(0)
+{
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        useTextureUnit_[i] = false;
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        constantBufferSizes_[i] = 0;
+}
+
+ShaderVariation::~ShaderVariation()
+{
+    Release();
+}
+
+bool ShaderVariation::Create()
+{
+    Release();
+    
+    if (!graphics_)
+        return false;
+    
+    if (!owner_)
+    {
+        compilerOutput_ = "Owner shader has expired";
+        return false;
+    }
+
+    // Check for up-to-date bytecode on disk
+    String path, name, extension;
+    SplitPath(owner_->GetName(), path, name, extension);
+    extension = type_ == VS ? ".vs4" : ".ps4";
+    
+    String binaryShaderName = path + "Cache/" + name + "_" + StringHash(defines_).ToString() + extension;
+    
+    if (!LoadByteCode(binaryShaderName))
+    {
+        // Compile shader if don't have valid bytecode
+        if (!Compile())
+            return false;
+        // Save the bytecode after successful compile, but not if the source is from a package
+        if (owner_->GetTimeStamp())
+            SaveByteCode(binaryShaderName);
+    }
+    
+    // Then create shader from the bytecode
+    ID3D11Device* device = graphics_->GetImpl()->GetDevice();
+    if (type_ == VS)
+    {
+        if (device && byteCode_.Size())
+            device->CreateVertexShader(&byteCode_[0], byteCode_.Size(), 0, (ID3D11VertexShader**)&object_);
+        if (!object_)
+            compilerOutput_ = "Could not create vertex shader";
+    }
+    else
+    {
+        if (device && byteCode_.Size())
+            device->CreatePixelShader(&byteCode_[0], byteCode_.Size(), 0, (ID3D11PixelShader**)&object_);
+        if (!object_)
+            compilerOutput_ = "Could not create pixel shader";
+    }
+
+    return object_ != 0;
+}
+
+void ShaderVariation::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        graphics_->CleanUpShaderPrograms(this);
+
+        if (type_ == VS)
+        {
+            if (graphics_->GetVertexShader() == this)
+                graphics_->SetShaders(0, 0);
+            
+            ((ID3D11VertexShader*)object_)->Release();
+        }
+        else
+        {
+            if (graphics_->GetPixelShader() == this)
+                graphics_->SetShaders(0, 0);
+            
+            ((ID3D11PixelShader*)object_)->Release();
+        }
+
+        object_ = 0;
+    }
+    
+    compilerOutput_.Clear();
+    
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        useTextureUnit_[i] = false;
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        constantBufferSizes_[i] = 0;
+    parameters_.Clear();
+    byteCode_.Clear();
+    elementMask_ = 0;
+}
+
+void ShaderVariation::SetName(const String& name)
+{
+    name_ = name;
+}
+
+void ShaderVariation::SetDefines(const String& defines)
+{
+    defines_ = defines;
+}
+
+Shader* ShaderVariation::GetOwner() const
+{
+    return owner_;
+}
+
+bool ShaderVariation::LoadByteCode(const String& binaryShaderName)
+{
+    ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();
+    if (!cache->Exists(binaryShaderName))
+        return false;
+    
+    FileSystem* fileSystem = owner_->GetSubsystem<FileSystem>();
+    unsigned sourceTimeStamp = owner_->GetTimeStamp();
+    // If source code is loaded from a package, its timestamp will be zero. Else check that binary is not older
+    // than source
+    if (sourceTimeStamp && fileSystem->GetLastModifiedTime(cache->GetResourceFileName(binaryShaderName)) <
+        sourceTimeStamp)
+        return false;
+    
+    SharedPtr<File> file = cache->GetFile(binaryShaderName);
+    if (!file || file->ReadFileID() != "USHD")
+    {
+        LOGERROR(binaryShaderName + " is not a valid shader bytecode file");
+        return false;
+    }
+    
+    /// \todo Check that shader type and model match
+    unsigned short shaderType = file->ReadUShort();
+    unsigned short shaderModel = file->ReadUShort();
+    elementMask_ = file->ReadUInt();
+    
+    unsigned numParameters = file->ReadUInt();
+    for (unsigned i = 0; i < numParameters; ++i)
+    {
+        String name = file->ReadString();
+        unsigned buffer = file->ReadUByte();
+        unsigned offset = file->ReadUInt();
+        unsigned size = file->ReadUInt();
+
+        ShaderParameter parameter(type_, name_, buffer, offset, size);
+        parameters_[StringHash(name)] = parameter;
+    }
+    
+    unsigned numTextureUnits = file->ReadUInt();
+    for (unsigned i = 0; i < numTextureUnits; ++i)
+    {
+        String unitName = file->ReadString();
+        unsigned reg = file->ReadUByte();
+        
+        if (reg < MAX_TEXTURE_UNITS)
+            useTextureUnit_[reg] = true;
+    }
+    
+    unsigned byteCodeSize = file->ReadUInt();
+    if (byteCodeSize)
+    {
+        byteCode_.Resize(byteCodeSize);
+        file->Read(&byteCode_[0], byteCodeSize);
+        
+        if (type_ == VS)
+            LOGDEBUG("Loaded cached vertex shader " + GetFullName());
+        else
+            LOGDEBUG("Loaded cached pixel shader " + GetFullName());
+        
+        CalculateConstantBufferSizes();
+        return true;
+    }
+    else
+    {
+        LOGERROR(binaryShaderName + " has zero length bytecode");
+        return false;
+    }
+}
+
+bool ShaderVariation::Compile()
+{
+    const String& sourceCode = owner_->GetSourceCode(type_);
+    Vector<String> defines = defines_.Split(' ');
+    
+    // Set the entrypoint, profile and flags according to the shader being compiled
+    const char* entryPoint = 0;
+    const char* profile = 0;
+    unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
+    
+    defines.Push("D3D11");
+
+    if (type_ == VS)
+    {
+        entryPoint = "VS";
+        defines.Push("COMPILEVS");
+        profile = "vs_4_0";
+    }
+    else
+    {
+        entryPoint = "PS";
+        defines.Push("COMPILEPS");
+        profile = "ps_4_0";
+        flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
+    }
+
+    defines.Push("MAXBONES=" + String(Graphics::GetMaxBones()));
+    
+    // Collect defines into macros
+    Vector<String> defineValues;
+    PODVector<D3D_SHADER_MACRO> macros;
+    
+    for (unsigned i = 0; i < defines.Size(); ++i)
+    {
+        unsigned equalsPos = defines[i].Find('=');
+        if (equalsPos != String::NPOS)
+        {
+            defineValues.Push(defines[i].Substring(equalsPos + 1));
+            defines[i].Resize(equalsPos);
+        }
+        else
+            defineValues.Push("1");
+    }
+    for (unsigned i = 0; i < defines.Size(); ++i)
+    {
+        D3D_SHADER_MACRO macro;
+        macro.Name = defines[i].CString();
+        macro.Definition = defineValues[i].CString();
+        macros.Push(macro);
+
+        // In debug mode, check that all defines are referenced by the shader code
+        #ifdef _DEBUG
+        if (sourceCode.Find(defines[i]) == String::NPOS)
+            LOGWARNING("Shader " + GetFullName() + " does not use the define " + defines[i]);
+        #endif
+    }
+    
+    D3D_SHADER_MACRO endMacro;
+    endMacro.Name = 0;
+    endMacro.Definition = 0;
+    macros.Push(endMacro);
+    
+    // Compile using D3DCompile
+    ID3DBlob* shaderCode = 0;
+    ID3DBlob* errorMsgs = 0;
+    
+    if (FAILED(D3DCompile(sourceCode.CString(), sourceCode.Length(), owner_->GetName().CString(), &macros.Front(), 0,
+        entryPoint, profile, flags, 0, &shaderCode, &errorMsgs)))
+        compilerOutput_ = String((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize());
+    else
+    {
+        if (type_ == VS)
+            LOGDEBUG("Compiled vertex shader " + GetFullName());
+        else
+            LOGDEBUG("Compiled pixel shader " + GetFullName());
+        
+        unsigned char* bufData = (unsigned char*)shaderCode->GetBufferPointer();
+        unsigned bufSize = shaderCode->GetBufferSize();
+        // Use the original bytecode to reflect the parameters
+        ParseParameters(bufData, bufSize);
+        CalculateConstantBufferSizes();
+
+        // Then strip everything not necessary to use the shader
+        ID3DBlob* strippedCode = 0;
+        D3DStripShader(bufData, bufSize, D3DCOMPILER_STRIP_REFLECTION_DATA | D3DCOMPILER_STRIP_DEBUG_INFO |
+            D3DCOMPILER_STRIP_TEST_BLOBS, &strippedCode);
+        byteCode_.Resize(strippedCode->GetBufferSize());
+        memcpy(&byteCode_[0], strippedCode->GetBufferPointer(), byteCode_.Size());
+        strippedCode->Release();
+    }
+
+    if (shaderCode)
+        shaderCode->Release();
+    if (errorMsgs)
+        errorMsgs->Release();
+    
+    return !byteCode_.Empty();
+}
+
+void ShaderVariation::ParseParameters(unsigned char* bufData, unsigned bufSize)
+{
+    ID3D11ShaderReflection* reflection = 0;
+    D3D11_SHADER_DESC shaderDesc;
+
+    D3DReflect(bufData, bufSize, IID_ID3D11ShaderReflection, (void**)&reflection);
+    if (!reflection)
+    {
+        LOGERROR("Failed to reflect vertex shader's input signature");
+        return;
+    }
+
+    reflection->GetDesc(&shaderDesc);
+
+    if (type_ == VS)
+    {
+        for (unsigned i = 0; i < shaderDesc.InputParameters; ++i)
+        {
+            D3D11_SIGNATURE_PARAMETER_DESC paramDesc;
+            reflection->GetInputParameterDesc((unsigned)i, &paramDesc);
+            for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j)
+            {
+                if (!String::Compare(paramDesc.SemanticName, VertexBuffer::elementSemantics[j], true) && paramDesc.SemanticIndex ==
+                    VertexBuffer::elementSemanticIndices[j])
+                {
+                    elementMask_ |= (1 << j);
+                    break;
+                }
+            }
+        }
+    }
+
+    HashMap<String, unsigned> cbRegisterMap;
+
+    for (unsigned i = 0; i < shaderDesc.BoundResources; ++i)
+    {
+        D3D11_SHADER_INPUT_BIND_DESC resourceDesc;
+        reflection->GetResourceBindingDesc(i, &resourceDesc);
+        String resourceName(resourceDesc.Name);
+        if (resourceDesc.Type == D3D_SIT_CBUFFER)
+            cbRegisterMap[resourceName] = resourceDesc.BindPoint;
+        else if (type_ == PS && resourceDesc.Type == D3D_SIT_SAMPLER && resourceDesc.BindPoint < MAX_TEXTURE_UNITS)
+            useTextureUnit_[resourceDesc.BindPoint] = true;
+    }
+
+    for (unsigned i = 0; i < shaderDesc.ConstantBuffers; ++i)
+    {
+        ID3D11ShaderReflectionConstantBuffer* cb = reflection->GetConstantBufferByIndex(i);
+        D3D11_SHADER_BUFFER_DESC cbDesc;
+        cb->GetDesc(&cbDesc);
+        unsigned cbRegister = cbRegisterMap[String(cbDesc.Name)];
+
+        for (unsigned j = 0; j < cbDesc.Variables; ++j)
+        {
+            ID3D11ShaderReflectionVariable* var = cb->GetVariableByIndex(j);
+            D3D11_SHADER_VARIABLE_DESC varDesc;
+            var->GetDesc(&varDesc);
+            String varName(varDesc.Name);
+            if (varName[0] == 'c')
+            {
+                varName = varName.Substring(1); // Strip the c to follow Urho3D constant naming convention
+                parameters_[varName] = ShaderParameter(type_, varName, cbRegister, varDesc.StartOffset, varDesc.Size);
+            }
+        }
+    }
+
+    reflection->Release();
+}
+
+void ShaderVariation::SaveByteCode(const String& binaryShaderName)
+{
+    ResourceCache* cache = owner_->GetSubsystem<ResourceCache>();
+    FileSystem* fileSystem = owner_->GetSubsystem<FileSystem>();
+    
+    String path = GetPath(cache->GetResourceFileName(owner_->GetName())) + "Cache/";
+    String fullName = path + GetFileNameAndExtension(binaryShaderName);
+    if (!fileSystem->DirExists(path))
+        fileSystem->CreateDir(path);
+    
+    SharedPtr<File> file(new File(owner_->GetContext(), fullName, FILE_WRITE));
+    if (!file->IsOpen())
+        return;
+    
+    file->WriteFileID("USHD");
+    file->WriteShort((unsigned short)type_);
+    file->WriteShort(4);
+    file->WriteUInt(elementMask_);
+
+    file->WriteUInt(parameters_.Size());
+    for (HashMap<StringHash, ShaderParameter>::ConstIterator i = parameters_.Begin(); i != parameters_.End(); ++i)
+    {
+        file->WriteString(i->second_.name_);
+        file->WriteUByte((unsigned char)i->second_.buffer_);
+        file->WriteUInt(i->second_.offset_);
+        file->WriteUInt(i->second_.size_);
+    }
+    
+    unsigned usedTextureUnits = 0;
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        if (useTextureUnit_[i])
+            ++usedTextureUnits;
+    }
+    file->WriteUInt(usedTextureUnits);
+    for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+    {
+        if (useTextureUnit_[i])
+        {
+            file->WriteString(graphics_->GetTextureUnitName((TextureUnit)i));
+            file->WriteUByte(i);
+        }
+    }
+    
+    file->WriteUInt(byteCode_.Size());
+    if (byteCode_.Size())
+        file->Write(&byteCode_[0], byteCode_.Size());
+}
+
+void ShaderVariation::CalculateConstantBufferSizes()
+{
+    for (unsigned i = 0; i < MAX_SHADER_PARAMETER_GROUPS; ++i)
+        constantBufferSizes_[i] = 0;
+    
+    for (HashMap<StringHash, ShaderParameter>::ConstIterator i = parameters_.Begin(); i != parameters_.End(); ++i)
+    {
+        if (i->second_.buffer_ < MAX_SHADER_PARAMETER_GROUPS)
+        {
+            unsigned oldSize = constantBufferSizes_[i->second_.buffer_];
+            unsigned paramEnd = i->second_.offset_ + i->second_.size_;
+            if (paramEnd > oldSize)
+                constantBufferSizes_[i->second_.buffer_] = paramEnd;
+        }
+    }
+}
+
+}

+ 153 - 0
Source/Atomic/Graphics/Direct3D11/D3D11ShaderVariation.h

@@ -0,0 +1,153 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/HashSet.h"
+#include "../../Container/RefCounted.h"
+#include "../../Container/ArrayPtr.h"
+
+namespace Urho3D
+{
+
+class ConstantBuffer;
+class Shader;
+
+/// %Shader parameter definition.
+struct ShaderParameter
+{
+    /// Construct with defaults.
+    ShaderParameter() :
+        type_(VS),
+        buffer_(0),
+        offset_(0),
+        size_(0),
+        bufferPtr_(0)
+    {
+    }
+    
+    /// Construct with parameters.
+    ShaderParameter(ShaderType type, const String& name, unsigned buffer, unsigned offset, unsigned size, ConstantBuffer* ptr = 0) :
+        type_(type),
+        name_(name),
+        buffer_(buffer),
+        offset_(offset),
+        size_(size),
+        bufferPtr_(ptr)
+    {
+    }
+    
+    /// %Shader type.
+    ShaderType type_;
+    /// Name of the parameter.
+    String name_;
+    /// Constant buffer index.
+    unsigned buffer_;
+    /// Offset in constant buffer.
+    unsigned offset_;
+    /// Size of parameter in bytes.
+    unsigned size_;
+    /// Constant buffer pointer. Defined only in shader programs.
+    ConstantBuffer* bufferPtr_;
+};
+
+/// Vertex or pixel shader on the GPU.
+class URHO3D_API ShaderVariation : public RefCounted, public GPUObject
+{
+public:
+    /// Construct.
+    ShaderVariation(Shader* owner, ShaderType type);
+    /// Destruct.
+    virtual ~ShaderVariation();
+    
+    /// Release the shader.
+    virtual void Release();
+    
+    /// Compile the shader. Return true if successful.
+    bool Create();
+    /// Set name.
+    void SetName(const String& name);
+    /// Set defines.
+    void SetDefines(const String& defines);
+    
+    /// Return the owner resource.
+    Shader* GetOwner() const;
+    /// Return shader type.
+    ShaderType GetShaderType() const { return type_; }
+    /// Return shader name.
+    const String& GetName() const { return name_; }
+    /// Return full shader name.
+    String GetFullName() const { return name_ + "(" + defines_ + ")"; }
+    /// Return whether uses a parameter.
+    bool HasParameter(StringHash param) const { return parameters_.Contains(param); }
+    /// Return whether uses a texture unit (only for pixel shaders.)
+    bool HasTextureUnit(TextureUnit unit) const { return useTextureUnit_[unit]; }
+    /// Return all parameter definitions.
+    const HashMap<StringHash, ShaderParameter>& GetParameters() const { return parameters_; }
+    /// Return vertex element mask.
+    unsigned GetElementMask() const { return elementMask_; }
+    /// Return shader bytecode.
+    const PODVector<unsigned char>& GetByteCode() const { return byteCode_; }
+    /// Return defines.
+    const String& GetDefines() const { return defines_; }
+    /// Return compile error/warning string.
+    const String& GetCompilerOutput() const { return compilerOutput_; }
+    /// Return constant buffer data sizes.
+    const unsigned* GetConstantBufferSizes() const { return &constantBufferSizes_[0]; }
+
+private:
+    /// Load bytecode from a file. Return true if successful.
+    bool LoadByteCode(const String& binaryShaderName);
+    /// Compile from source. Return true if successful.
+    bool Compile();
+    /// Inspect the constant parameters and input layout (if applicable) from the shader bytecode.
+    void ParseParameters(unsigned char* bufData, unsigned bufSize);
+    /// Save bytecode to a file.
+    void SaveByteCode(const String& binaryShaderName);
+    /// Calculate constant buffer sizes from parameters.
+    void CalculateConstantBufferSizes();
+    
+    /// Shader this variation belongs to.
+    WeakPtr<Shader> owner_;
+    /// Shader type.
+    ShaderType type_;
+    /// Vertex element mask for vertex shaders. Zero for pixel shaders.
+    unsigned elementMask_;
+    /// Shader parameters.
+    HashMap<StringHash, ShaderParameter> parameters_;
+    /// Texture unit use flags.
+    bool useTextureUnit_[MAX_TEXTURE_UNITS];
+    /// Constant buffer sizes. 0 if a constant buffer slot is not in use.
+    unsigned constantBufferSizes_[MAX_SHADER_PARAMETER_GROUPS];
+    /// Bytecode. Needed for inspecting the input signature and parameters.
+    PODVector<unsigned char> byteCode_;
+    /// Shader name.
+    String name_;
+    /// Defines to use in compiling.
+    String defines_;
+    /// Shader compile error string.
+    String compilerOutput_;
+};
+
+}

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

@@ -0,0 +1,435 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../IO/FileSystem.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/Material.h"
+#include "../../Core/Profiler.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Core/StringUtils.h"
+#include "../../Graphics/Texture.h"
+#include "../../Resource/XMLFile.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+static const char* addressModeNames[] =
+{
+    "wrap",
+    "mirror",
+    "clamp",
+    "border",
+    0
+};
+
+static const char* filterModeNames[] =
+{
+    "nearest",
+    "bilinear",
+    "trilinear",
+    "anisotropic",
+    "default",
+    0
+};
+
+static const D3D11_FILTER d3dFilterMode[] =
+{
+    D3D11_FILTER_MIN_MAG_MIP_POINT,
+    D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT,
+    D3D11_FILTER_MIN_MAG_MIP_LINEAR,
+    D3D11_FILTER_ANISOTROPIC,
+    D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT,
+    D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT,
+    D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR,
+    D3D11_FILTER_COMPARISON_ANISOTROPIC
+};
+
+static const D3D11_TEXTURE_ADDRESS_MODE d3dAddressMode[] = 
+{
+    D3D11_TEXTURE_ADDRESS_WRAP,
+    D3D11_TEXTURE_ADDRESS_MIRROR,
+    D3D11_TEXTURE_ADDRESS_CLAMP,
+    D3D11_TEXTURE_ADDRESS_BORDER
+};
+
+Texture::Texture(Context* context) :
+    Resource(context),
+    GPUObject(GetSubsystem<Graphics>()),
+    shaderResourceView_(0),
+    sampler_(0),
+    format_(DXGI_FORMAT_UNKNOWN),
+    usage_(TEXTURE_STATIC),
+    levels_(0),
+    requestedLevels_(0),
+    width_(0),
+    height_(0),
+    depth_(0),
+    shadowCompare_(false),
+    filterMode_(FILTER_DEFAULT),
+    sRGB_(false),
+    parametersDirty_(true)
+{
+    for (int i = 0; i < MAX_COORDS; ++i)
+        addressMode_[i] = ADDRESS_WRAP;
+    for (int i = 0; i < MAX_TEXTURE_QUALITY_LEVELS; ++i)
+        mipsToSkip_[i] = MAX_TEXTURE_QUALITY_LEVELS - 1 - i;
+}
+
+Texture::~Texture()
+{
+}
+
+void Texture::SetNumLevels(unsigned levels)
+{
+    if (usage_ > TEXTURE_RENDERTARGET)
+        requestedLevels_ = 1;
+    else
+        requestedLevels_ = levels;
+}
+
+void Texture::SetFilterMode(TextureFilterMode mode)
+{
+    filterMode_ = mode;
+    parametersDirty_ = true;
+}
+
+void Texture::SetAddressMode(TextureCoordinate coord, TextureAddressMode mode)
+{
+    addressMode_[coord] = mode;
+    parametersDirty_ = true;
+}
+
+void Texture::SetShadowCompare(bool enable)
+{
+    shadowCompare_ = enable;
+    parametersDirty_ = true;
+}
+
+void Texture::SetBorderColor(const Color& color)
+{
+    borderColor_ = color;
+    parametersDirty_ = true;
+}
+
+void Texture::SetSRGB(bool enable)
+{
+    if (graphics_)
+        enable &= graphics_->GetSRGBSupport();
+    
+    // Note: on D3D11 sRGB only affects the texture before creation
+    sRGB_ = enable;
+}
+
+void Texture::SetBackupTexture(Texture* texture)
+{
+    backupTexture_ = texture;
+}
+
+void Texture::SetMipsToSkip(int quality, int mips)
+{
+    if (quality >= QUALITY_LOW && quality < MAX_TEXTURE_QUALITY_LEVELS)
+    {
+        mipsToSkip_[quality] = mips;
+        
+        // Make sure a higher quality level does not actually skip more mips
+        for (int i = 1; i < MAX_TEXTURE_QUALITY_LEVELS; ++i)
+        {
+            if (mipsToSkip_[i] > mipsToSkip_[i - 1])
+                mipsToSkip_[i] = mipsToSkip_[i - 1];
+        }
+    }
+}
+
+bool Texture::IsCompressed() const
+{
+    return format_ == DXGI_FORMAT_BC1_UNORM || format_ == DXGI_FORMAT_BC2_UNORM || format_ == DXGI_FORMAT_BC3_UNORM;
+}
+
+int Texture::GetMipsToSkip(int quality) const
+{
+    return (quality >= QUALITY_LOW && quality < MAX_TEXTURE_QUALITY_LEVELS) ? mipsToSkip_[quality] : 0;
+}
+
+int Texture::GetLevelWidth(unsigned level) const
+{
+    if (level > levels_)
+        return 0;
+    return Max(width_ >> level, 1);
+}
+
+int Texture::GetLevelHeight(unsigned level) const
+{
+    if (level > levels_)
+        return 0;
+    return Max(height_ >> level, 1);
+}
+
+int Texture::GetLevelDepth(unsigned level) const
+{
+    if (level > levels_)
+        return 0;
+    return Max(depth_ >> level, 1);
+}
+
+unsigned Texture::GetDataSize(int width, int height) const
+{
+    if (IsCompressed())
+        return GetRowDataSize(width) * ((height + 3) >> 2);
+    else
+        return GetRowDataSize(width) * height;
+}
+
+unsigned Texture::GetDataSize(int width, int height, int depth) const
+{
+    return depth * GetDataSize(width, height);
+}
+
+unsigned Texture::GetRowDataSize(int width) const
+{
+    switch (format_)
+    {
+    case DXGI_FORMAT_R8_UNORM:
+    case DXGI_FORMAT_A8_UNORM:
+        return width;
+    
+    case DXGI_FORMAT_R8G8_UNORM:
+    case DXGI_FORMAT_R16_UNORM:
+    case DXGI_FORMAT_R16_FLOAT:
+    case DXGI_FORMAT_R16_TYPELESS:
+        return width * 2;
+
+    case DXGI_FORMAT_R8G8B8A8_UNORM:
+    case DXGI_FORMAT_R16G16_UNORM:
+    case DXGI_FORMAT_R16G16_FLOAT:
+    case DXGI_FORMAT_R32_FLOAT:
+    case DXGI_FORMAT_R24G8_TYPELESS:
+    case DXGI_FORMAT_R32_TYPELESS:
+        return width * 4;
+        
+    case DXGI_FORMAT_R16G16B16A16_UNORM:
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+        return width * 8;
+        
+    case DXGI_FORMAT_R32G32B32A32_FLOAT:
+        return width * 16;
+        
+    case DXGI_FORMAT_BC1_UNORM:
+        return ((width + 3) >> 2) * 8;
+        
+    case DXGI_FORMAT_BC2_UNORM:
+    case DXGI_FORMAT_BC3_UNORM:
+        return ((width + 3) >> 2) * 16;
+        
+    default:
+        return 0;
+    }
+}
+
+void Texture::SetParameters(XMLFile* file)
+{
+    if (!file)
+        return;
+    
+    XMLElement rootElem = file->GetRoot();
+    SetParameters(rootElem);
+}
+
+void Texture::SetParameters(const XMLElement& element)
+{
+    XMLElement paramElem = element.GetChild();
+    while (paramElem)
+    {
+        String name = paramElem.GetName();
+        
+        if (name == "address")
+        {
+            String coord = paramElem.GetAttributeLower("coord");
+            if (coord.Length() >= 1)
+            {
+                TextureCoordinate coordIndex = (TextureCoordinate)(coord[0] - 'u');
+                String mode = paramElem.GetAttributeLower("mode");
+                SetAddressMode(coordIndex, (TextureAddressMode)GetStringListIndex(mode.CString(), addressModeNames, ADDRESS_WRAP));
+            }
+        }
+        
+        if (name == "border")
+            SetBorderColor(paramElem.GetColor("color"));
+        
+        if (name == "filter")
+        {
+            String mode = paramElem.GetAttributeLower("mode");
+            SetFilterMode((TextureFilterMode)GetStringListIndex(mode.CString(), filterModeNames, FILTER_DEFAULT));
+        }
+        
+        if (name == "mipmap")
+            SetNumLevels(paramElem.GetBool("enable") ? 0 : 1);
+        
+        if (name == "quality")
+        {
+            if (paramElem.HasAttribute("low"))
+                SetMipsToSkip(QUALITY_LOW, paramElem.GetInt("low"));
+            if (paramElem.HasAttribute("med"))
+                SetMipsToSkip(QUALITY_MEDIUM, paramElem.GetInt("med"));
+            if (paramElem.HasAttribute("medium"))
+                SetMipsToSkip(QUALITY_MEDIUM, paramElem.GetInt("medium"));
+            if (paramElem.HasAttribute("high"))
+                SetMipsToSkip(QUALITY_HIGH, paramElem.GetInt("high"));
+        }
+
+        if (name == "srgb")
+            SetSRGB(paramElem.GetBool("enable"));
+        
+        paramElem = paramElem.GetNext();
+    }
+}
+
+void Texture::SetParametersDirty()
+{
+    parametersDirty_ = true;
+}
+
+void Texture::UpdateParameters()
+{
+    if ((!parametersDirty_ && sampler_) || !object_)
+        return;
+
+    // Release old sampler
+    if (sampler_)
+    {
+        ((ID3D11SamplerState*)sampler_)->Release();
+        sampler_ = 0;
+    }
+
+    D3D11_SAMPLER_DESC samplerDesc;
+    memset(&samplerDesc, 0, sizeof samplerDesc);
+    unsigned filterModeIndex = filterMode_ != FILTER_DEFAULT ? filterMode_ : graphics_->GetDefaultTextureFilterMode();
+    if (shadowCompare_)
+        filterModeIndex += 4;
+    samplerDesc.Filter = d3dFilterMode[filterModeIndex];
+    samplerDesc.AddressU = d3dAddressMode[addressMode_[0]];
+    samplerDesc.AddressV = d3dAddressMode[addressMode_[1]];
+    samplerDesc.AddressW = d3dAddressMode[addressMode_[2]];
+    samplerDesc.MaxAnisotropy = graphics_->GetTextureAnisotropy();
+    samplerDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
+    samplerDesc.MinLOD = -M_INFINITY;
+    samplerDesc.MaxLOD = M_INFINITY;
+    memcpy(&samplerDesc.BorderColor, borderColor_.Data(), 4 * sizeof(float));
+
+    graphics_->GetImpl()->GetDevice()->CreateSamplerState(&samplerDesc, (ID3D11SamplerState**)&sampler_);
+
+    if (!sampler_)
+        LOGERROR("Failed to create sampler state");
+
+    parametersDirty_ = false;
+}
+
+unsigned Texture::CheckMaxLevels(int width, int height, unsigned requestedLevels)
+{
+    unsigned maxLevels = 1;
+    while (width > 1 && height > 1)
+    {
+        ++maxLevels;
+        width >>= 1;
+        height >>= 1;
+    }
+
+    if (!requestedLevels || maxLevels < requestedLevels)
+        return maxLevels;
+    else
+        return requestedLevels;
+}
+
+unsigned Texture::CheckMaxLevels(int width, int height, int depth, unsigned requestedLevels)
+{
+    unsigned maxLevels = 1;
+    while (width > 1 && height > 1 && depth > 1)
+    {
+        ++maxLevels;
+        width >>= 1;
+        height >>= 1;
+        depth >>= 1;
+    }
+
+    if (!requestedLevels || maxLevels < requestedLevels)
+        return maxLevels;
+    else
+        return requestedLevels;
+}
+
+
+unsigned Texture::GetSRVFormat(unsigned format)
+{
+    if (format == DXGI_FORMAT_R24G8_TYPELESS)
+        return DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
+    else if (format == DXGI_FORMAT_R16_TYPELESS)
+        return DXGI_FORMAT_R16_UNORM;
+    else if (format == DXGI_FORMAT_R32_TYPELESS)
+        return DXGI_FORMAT_R32_FLOAT;
+    else
+        return format;
+}
+
+unsigned Texture::GetDSVFormat(unsigned format)
+{
+    if (format == DXGI_FORMAT_R24G8_TYPELESS)
+        return DXGI_FORMAT_D24_UNORM_S8_UINT;
+    else if (format == DXGI_FORMAT_R16_TYPELESS)
+        return DXGI_FORMAT_D16_UNORM;
+    else if (format == DXGI_FORMAT_R32_TYPELESS)
+        return DXGI_FORMAT_D32_FLOAT;
+    else
+        return format;
+}
+
+unsigned Texture::GetSRGBFormat(unsigned format)
+{
+    if (format == DXGI_FORMAT_R8G8B8A8_UNORM)
+        return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+    else if (format == DXGI_FORMAT_BC1_UNORM)
+        return DXGI_FORMAT_BC1_UNORM_SRGB;
+    else if (format == DXGI_FORMAT_BC2_UNORM)
+        return DXGI_FORMAT_BC2_UNORM_SRGB;
+    else if (format == DXGI_FORMAT_BC3_UNORM)
+        return DXGI_FORMAT_BC3_UNORM_SRGB;
+    else
+        return format;
+}
+
+void Texture::CheckTextureBudget(StringHash type)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    unsigned textureBudget = cache->GetMemoryBudget(type);
+    unsigned textureUse = cache->GetMemoryUse(type);
+    if (!textureBudget)
+        return;
+    
+    // If textures are over the budget, they likely can not be freed directly as materials still refer to them.
+    // Therefore free unused materials first
+    if (textureUse > textureBudget)
+        cache->ReleaseResources(Material::GetTypeStatic());
+}
+
+}

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

@@ -0,0 +1,172 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/ArrayPtr.h"
+#include "../../Math/Color.h"
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Resource/Resource.h"
+
+namespace Urho3D
+{
+
+static const int MAX_TEXTURE_QUALITY_LEVELS = 3;
+
+class XMLElement;
+class XMLFile;
+
+/// Base class for texture resources.
+class URHO3D_API Texture : public Resource, public GPUObject
+{
+public:
+    /// Construct.
+    Texture(Context* context);
+    /// Destruct.
+    virtual ~Texture();
+    
+    /// Set number of requested mip levels. Needs to be called before setting size.
+    void SetNumLevels(unsigned levels);
+    /// Set filtering mode.
+    void SetFilterMode(TextureFilterMode filter);
+    /// Set addressing mode by texture coordinate.
+    void SetAddressMode(TextureCoordinate coord, TextureAddressMode address);
+    /// Set shadow compare mode.
+    void SetShadowCompare(bool enable);
+    /// Set border color for border addressing mode.
+    void SetBorderColor(const Color& color);
+    /// Set sRGB sampling and writing mode.
+    void SetSRGB(bool enable);
+    /// Set backup texture to use when rendering to this texture.
+    void SetBackupTexture(Texture* texture);
+    /// Set mip levels to skip on a quality setting when loading. Ensures higher quality levels do not skip more.
+    void SetMipsToSkip(int quality, int mips);
+    
+    /// Return texture format.
+    unsigned GetFormat() const { return format_; }
+    /// Return whether the texture format is compressed.
+    bool IsCompressed() const;
+    /// Return number of mip levels.
+    unsigned GetLevels() const { return levels_; }
+    /// Return width.
+    int GetWidth() const { return width_; }
+    /// Return height.
+    int GetHeight() const { return height_; }
+    /// Return height.
+    int GetDepth() const { return depth_; }
+    /// Return filtering mode.
+    TextureFilterMode GetFilterMode() const { return filterMode_; }
+    /// Return addressing mode by texture coordinate.
+    TextureAddressMode GetAddressMode(TextureCoordinate coord) const { return addressMode_[coord]; }
+    /// Return whether shadow compare is enabled.
+    bool GetShadowCompare() const { return shadowCompare_; }
+    /// Return border color.
+    const Color& GetBorderColor() const { return borderColor_; }
+    /// Return whether is using sRGB sampling and writing.
+    bool GetSRGB() const { return sRGB_; }
+    /// Return backup texture.
+    Texture* GetBackupTexture() const { return backupTexture_; }
+    /// Return mip levels to skip on a quality setting when loading.
+    int GetMipsToSkip(int quality) const;
+    /// Return mip level width, or 0 if level does not exist.
+    int GetLevelWidth(unsigned level) const;
+    /// Return mip level width, or 0 if level does not exist.
+    int GetLevelHeight(unsigned level) const;
+    /// Return mip level depth, or 0 if level does not exist.
+    int GetLevelDepth(unsigned level) const;
+    /// Return texture usage type.
+    TextureUsage GetUsage() const { return usage_; }
+    /// Return data size in bytes for a rectangular region.
+    unsigned GetDataSize(int width, int height) const;
+    /// Return data size in bytes for a volume region.
+    unsigned GetDataSize(int width, int height, int depth) const;
+    /// Return data size in bytes for a pixel or block row.
+    unsigned GetRowDataSize(int width) const;
+    /// Return whether the parameters are dirty.
+    bool GetParametersDirty() const { return parametersDirty_ || !sampler_; }
+
+    /// Set additional parameters from an XML file.
+    void SetParameters(XMLFile* xml);
+    /// Set additional parameters from an XML element.
+    void SetParameters(const XMLElement& element);
+    /// Mark parameters dirty. Called by Graphics.
+    void SetParametersDirty();
+    /// Create sampler state object after parameters have been changed. Called by Graphics when assigning the texture.
+    void UpdateParameters();
+    /// Return shader resource view.
+    void* GetShaderResourceView() const { return shaderResourceView_; }
+    /// Return sampler state object.
+    void* GetSampler() const { return sampler_; }
+    
+    /// Check maximum allowed mip levels for a specific texture size.
+    static unsigned CheckMaxLevels(int width, int height, unsigned requestedLevels);
+    /// Check maximum allowed mip levels for a specific 3D texture size.
+    static unsigned CheckMaxLevels(int width, int height, int depth, unsigned requestedLevels);
+    /// Return the shader resource view format corresponding to a texture format. Handles conversion of typeless depth texture formats.
+    static unsigned GetSRVFormat(unsigned format);
+    /// Return the depth-stencil view format corresponding to a texture format. Handles conversion of typeless depth texture formats.
+    static unsigned GetDSVFormat(unsigned format);
+    /// Convert format to sRGB.
+    static unsigned GetSRGBFormat(unsigned format);
+
+protected:
+    /// Check whether texture memory budget has been exceeded. Free unused materials in that case to release the texture references.
+    void CheckTextureBudget(StringHash type);
+    
+    /// Shader resource view.
+    void* shaderResourceView_;
+    /// Sampler state object.
+    void* sampler_;
+    /// Texture format.
+    unsigned format_;
+    /// Texture usage type.
+    TextureUsage usage_;
+    /// Current mip levels.
+    unsigned levels_;
+    /// Requested mip levels.
+    unsigned requestedLevels_;
+    /// Texture width.
+    int width_;
+    /// Texture height.
+    int height_;
+    /// Texture depth.
+    int depth_;
+    /// Shadow compare mode.
+    bool shadowCompare_;
+    /// Filtering mode.
+    TextureFilterMode filterMode_;
+    /// Addressing mode.
+    TextureAddressMode addressMode_[MAX_COORDS];
+    /// Mip levels to skip when loading per texture quality setting.
+    unsigned mipsToSkip_[MAX_TEXTURE_QUALITY_LEVELS];
+    /// Border color.
+    Color borderColor_;
+    /// sRGB sampling and writing mode flag.
+    bool sRGB_;
+    /// Parameters dirty flag.
+    bool parametersDirty_;
+    /// Backup texture.
+    SharedPtr<Texture> backupTexture_;
+};
+
+}

+ 541 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Texture2D.cpp

@@ -0,0 +1,541 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/Context.h"
+#include "../../IO/FileSystem.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Core/Profiler.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/Texture2D.h"
+#include "../../Resource/XMLFile.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+Texture2D::Texture2D(Context* context) :
+    Texture(context)
+{
+}
+
+Texture2D::~Texture2D()
+{
+    Release();
+}
+
+void Texture2D::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Texture2D>();
+}
+
+bool Texture2D::BeginLoad(Deserializer& source)
+{
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    // Load the image data for EndLoad()
+    loadImage_ = new Image(context_);
+    if (!loadImage_->Load(source))
+    {
+        loadImage_.Reset();
+        return false;
+    }
+
+    // Precalculate mip levels if async loading
+    if (GetAsyncLoadState() == ASYNC_LOADING)
+        loadImage_->PrecalculateLevels();
+    
+    // Load the optional parameters file
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    String xmlName = ReplaceExtension(GetName(), ".xml");
+    loadParameters_ = cache->GetTempResource<XMLFile>(xmlName, false);
+    
+    return true;
+}
+
+bool Texture2D::EndLoad()
+{
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    // If over the texture budget, see if materials can be freed to allow textures to be freed
+    CheckTextureBudget(GetTypeStatic());
+
+    SetParameters(loadParameters_);
+    bool success = SetData(loadImage_);
+    
+    loadImage_.Reset();
+    loadParameters_.Reset();
+    
+    return success;
+}
+
+void Texture2D::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        {
+            if (graphics_->GetTexture(i) == this)
+                graphics_->SetTexture(i, 0);
+        }
+        
+        if (renderSurface_)
+            renderSurface_->Release();
+        
+        ((ID3D11Resource*)object_)->Release();
+        object_ = 0;
+
+        if (shaderResourceView_)
+        {
+            ((ID3D11ShaderResourceView*)shaderResourceView_)->Release();
+            shaderResourceView_ = 0;
+        }
+
+        if (sampler_)
+        {
+            ((ID3D11SamplerState*)sampler_)->Release();
+            sampler_ = 0;
+        }
+    }
+    else
+    {
+        if (renderSurface_)
+            renderSurface_->Release();
+    }
+}
+
+bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usage)
+{
+    // Delete the old rendersurface if any
+    renderSurface_.Reset();
+    usage_ = usage;
+    
+    if (usage_ == TEXTURE_RENDERTARGET || usage_ == TEXTURE_DEPTHSTENCIL)
+    {
+        renderSurface_ = new RenderSurface(this);
+        // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
+        addressMode_[COORD_U] = ADDRESS_CLAMP;
+        addressMode_[COORD_V] = ADDRESS_CLAMP;
+        filterMode_ = FILTER_NEAREST;
+        requestedLevels_ = 1;
+    }
+    
+    if (usage_ == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(Texture2D, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+
+    width_ = width;
+    height_ = height;
+    format_ = format;
+    
+    return Create();
+}
+
+bool Texture2D::SetData(unsigned level, int x, int y, int width, int height, const void* data)
+{
+    PROFILE(SetTextureData);
+    
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not set data");
+        return false;
+    }
+    
+    if (!data)
+    {
+        LOGERROR("Null source for setting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for setting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0)
+    {
+        LOGERROR("Illegal dimensions for setting data");
+        return false;
+    }
+    
+    // If compressed, align the update region on a block
+    if (IsCompressed())
+    {
+        x &= ~3;
+        y &= ~3;
+        width += 3;
+        width &= 0xfffffffc;
+        height += 3;
+        height &= 0xfffffffc;
+    }
+
+    unsigned char* src = (unsigned char*)data;
+    unsigned rowSize = GetRowDataSize(width);
+    unsigned rowStart = GetRowDataSize(x);
+    unsigned subResource = D3D11CalcSubresource(level, 0, levels_);
+
+    if (usage_ == TEXTURE_DYNAMIC)
+    {
+        if (IsCompressed())
+        {
+            height = (height + 3) >> 2;
+            y >>= 2;
+        }
+
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)object_, subResource, D3D11_MAP_WRITE_DISCARD, 0,
+            &mappedData);
+        if (mappedData.pData)
+        {
+            for (int row = 0; row < height; ++row)
+                memcpy((unsigned char*)mappedData.pData + (row + y) * mappedData.RowPitch + rowStart, src + row * rowSize, rowSize);
+            graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)object_, subResource);
+        }
+        else
+        {
+            LOGERROR("Failed to map texture for update");
+            return false;
+        }
+    }
+    else
+    {
+        D3D11_BOX destBox;
+        destBox.left = x;
+        destBox.right = x + width;
+        destBox.top = y;
+        destBox.bottom = y + height;
+        destBox.front = 0;
+        destBox.back = 1;
+
+        graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Resource*)object_, subResource, &destBox, data,
+            rowSize, 0);
+    }
+
+    return true;
+}
+
+bool Texture2D::SetData(SharedPtr<Image> image, bool useAlpha)
+{
+    if (!image)
+    {
+        LOGERROR("Null image, can not load texture");
+        return false;
+    }
+    
+    unsigned memoryUse = sizeof(Texture2D);
+    
+    int quality = QUALITY_HIGH;
+    Renderer* renderer = GetSubsystem<Renderer>();
+    if (renderer)
+        quality = renderer->GetTextureQuality();
+    
+    if (!image->IsCompressed())
+    {
+        // Convert unsuitable formats to RGBA
+        unsigned components = image->GetComponents();
+        if ((components == 1 && !useAlpha) || components == 2 || components == 3)
+        {
+            image = image->ConvertToRGBA();
+            if (!image)
+                return false;
+            components = image->GetComponents();
+        }
+
+        unsigned char* levelData = image->GetData();
+        int levelWidth = image->GetWidth();
+        int levelHeight = image->GetHeight();
+        unsigned format = 0;
+        
+        // Discard unnecessary mip levels
+        for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
+        {
+            image = image->GetNextLevel();
+            levelData = image->GetData();
+            levelWidth = image->GetWidth();
+            levelHeight = image->GetHeight();
+        }
+        
+        switch (components)
+        {
+        case 1:
+            format = Graphics::GetAlphaFormat();
+            break;
+            
+        case 4:
+            format = Graphics::GetRGBAFormat();
+            break;
+        }
+        
+        // If image was previously compressed, reset number of requested levels to avoid error if level count is too high for new size
+        if (IsCompressed() && requestedLevels_ > 1)
+            requestedLevels_ = 0;
+        SetSize(levelWidth, levelHeight, format);
+        
+        for (unsigned i = 0; i < levels_; ++i)
+        {
+            SetData(i, 0, 0, levelWidth, levelHeight, levelData);
+            memoryUse += levelWidth * levelHeight * components;
+            
+            if (i < levels_ - 1)
+            {
+                image = image->GetNextLevel();
+                levelData = image->GetData();
+                levelWidth = image->GetWidth();
+                levelHeight = image->GetHeight();
+            }
+        }
+    }
+    else
+    {
+        int width = image->GetWidth();
+        int height = image->GetHeight();
+        unsigned levels = image->GetNumCompressedLevels();
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
+        bool needDecompress = false;
+        
+        if (!format)
+        {
+            format = Graphics::GetRGBAFormat();
+            needDecompress = true;
+        }
+        
+        unsigned mipsToSkip = mipsToSkip_[quality];
+        if (mipsToSkip >= levels)
+            mipsToSkip = levels - 1;
+        while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
+            --mipsToSkip;
+        width /= (1 << mipsToSkip);
+        height /= (1 << mipsToSkip);
+        
+        SetNumLevels(Max((int)(levels - mipsToSkip), 1));
+        SetSize(width, height, format);
+        
+        for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
+        {
+            CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
+            if (!needDecompress)
+            {
+                SetData(i, 0, 0, level.width_, level.height_, level.data_);
+                memoryUse += level.rows_ * level.rowSize_;
+            }
+            else
+            {
+                unsigned char* rgbaData = new unsigned char[level.width_ * level.height_ * 4];
+                level.Decompress(rgbaData);
+                SetData(i, 0, 0, level.width_, level.height_, rgbaData);
+                memoryUse += level.width_ * level.height_ * 4;
+                delete[] rgbaData;
+            }
+        }
+    }
+    
+    SetMemoryUse(memoryUse);
+    return true;
+}
+
+bool Texture2D::GetData(unsigned level, void* dest) const
+{
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not get data");
+        return false;
+    }
+    
+    if (!dest)
+    {
+        LOGERROR("Null destination for getting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for getting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = levelWidth;
+    textureDesc.Height = levelHeight;
+    textureDesc.MipLevels = 1;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = (DXGI_FORMAT)format_;
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = D3D11_USAGE_STAGING;
+    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+
+    ID3D11Texture2D* stagingTexture = 0;
+    graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
+    if (!stagingTexture)
+    {
+        LOGERROR("Failed to create staging texture for GetData");
+        return false;
+    }
+
+    unsigned srcSubResource = D3D11CalcSubresource(level, 0, levels_);
+    D3D11_BOX srcBox;
+    srcBox.left = 0;
+    srcBox.right = levelWidth;
+    srcBox.top = 0;
+    srcBox.bottom = levelHeight;
+    srcBox.front = 0;
+    srcBox.back = 1;
+    graphics_->GetImpl()->GetDeviceContext()->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, (ID3D11Resource*)object_,
+        srcSubResource, &srcBox);
+    
+    D3D11_MAPPED_SUBRESOURCE mappedData;
+    mappedData.pData = 0;
+    unsigned rowSize = GetRowDataSize(levelWidth);
+    unsigned numRows = IsCompressed() ? (levelHeight + 3) >> 2 : levelHeight;
+
+    graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)stagingTexture, 0, D3D11_MAP_READ, 0, &mappedData);
+    if (mappedData.pData)
+    {
+        for (unsigned row = 0; row < numRows; ++row)
+            memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
+        stagingTexture->Release();
+        return true;
+    }
+    else
+    {
+        LOGERROR("Failed to map staging texture for GetData");
+        stagingTexture->Release();
+        return false;
+    }
+}
+
+bool Texture2D::Create()
+{
+    Release();
+    
+    if (!graphics_ || !width_ || !height_)
+        return false;
+    
+    levels_ = CheckMaxLevels(width_, height_, requestedLevels_);
+    
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = width_;
+    textureDesc.Height = height_;
+    textureDesc.MipLevels = levels_;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    if (usage_ == TEXTURE_RENDERTARGET)
+        textureDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
+    else if (usage_ == TEXTURE_DEPTHSTENCIL)
+        textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
+    textureDesc.CPUAccessFlags = usage_ == TEXTURE_DYNAMIC ? D3D11_CPU_ACCESS_WRITE : 0;
+
+    graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_);
+    if (!object_)
+    {
+        LOGERROR("Failed to create texture");
+        return false;
+    }
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
+    memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
+    resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
+    resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    resourceViewDesc.Texture2D.MipLevels = (unsigned)levels_;
+
+    graphics_->GetImpl()->GetDevice()->CreateShaderResourceView((ID3D11Resource*)object_, &resourceViewDesc,
+        (ID3D11ShaderResourceView**)&shaderResourceView_);
+    if (!shaderResourceView_)
+    {
+        LOGERROR("Failed to create shader resource view for texture");
+        return false;
+    }
+
+    if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        renderSurface_ = new RenderSurface(this);
+
+        D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
+        memset(&renderTargetViewDesc, 0, sizeof renderTargetViewDesc);
+        renderTargetViewDesc.Format = textureDesc.Format;
+        renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+
+        graphics_->GetImpl()->GetDevice()->CreateRenderTargetView((ID3D11Resource*)object_, &renderTargetViewDesc,
+            (ID3D11RenderTargetView**)&renderSurface_->renderTargetView_);
+
+        if (!renderSurface_->renderTargetView_)
+        {
+            LOGERROR("Failed to create rendertarget view for texture");
+            return false;
+        }
+    }
+    else if (usage_ == TEXTURE_DEPTHSTENCIL)
+    {
+        renderSurface_ = new RenderSurface(this);
+
+        D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
+        memset(&depthStencilViewDesc, 0, sizeof depthStencilViewDesc);
+        depthStencilViewDesc.Format = (DXGI_FORMAT)GetDSVFormat(textureDesc.Format);
+        depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
+
+        graphics_->GetImpl()->GetDevice()->CreateDepthStencilView((ID3D11Resource*)object_, &depthStencilViewDesc,
+            (ID3D11DepthStencilView**)&renderSurface_->renderTargetView_);
+
+        if (!renderSurface_->renderTargetView_)
+        {
+            LOGERROR("Failed to create depth-stencil view for texture");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void Texture2D::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (renderSurface_ && renderSurface_->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+        renderSurface_->QueueUpdate();
+}
+
+}

+ 81 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Texture2D.h

@@ -0,0 +1,81 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/RenderSurface.h"
+#include "../../Container/Ptr.h"
+#include "../../Graphics/Texture.h"
+
+namespace Urho3D
+{
+
+class Image;
+class XMLFile;
+
+/// 2D texture resource.
+class URHO3D_API Texture2D : public Texture
+{
+    OBJECT(Texture2D);
+    
+public:
+    /// Construct.
+    Texture2D(Context* context);
+    /// Destruct.
+    virtual ~Texture2D();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Load resource from stream. May be called from a worker thread. Return true if successful.
+    virtual bool BeginLoad(Deserializer& source);
+    /// Finish resource loading. Always called from the main thread. Return true if successful.
+    virtual bool EndLoad();
+    /// Release texture.
+    virtual void Release();
+    
+    /// Set size, format and usage. Zero size will follow application window size. Return true if successful.
+    bool SetSize(int width, int height, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    /// Set data either partially or fully on a mip level. Return true if successful.
+    bool SetData(unsigned level, int x, int y, int width, int height, const void* data);
+    /// Set data from an image. Return true if successful. Optionally make a single channel image alpha-only.
+    bool SetData(SharedPtr<Image> image, bool useAlpha = false);
+    
+    /// Get data from a mip level. The destination buffer must be big enough. Return true if successful.
+    bool GetData(unsigned level, void* dest) const;
+    /// Return render surface.
+    RenderSurface* GetRenderSurface() const { return renderSurface_; }
+    
+private:
+    /// Create texture.
+    bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
+    
+    /// Render surface.
+    SharedPtr<RenderSurface> renderSurface_;
+    /// Image file acquired during BeginLoad.
+    SharedPtr<Image> loadImage_;
+    /// Parameter file acquired during BeginLoad.
+    SharedPtr<XMLFile> loadParameters_;
+};
+
+}

+ 565 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Texture3D.cpp

@@ -0,0 +1,565 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/Context.h"
+#include "../../IO/FileSystem.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Core/Profiler.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/Texture3D.h"
+#include "../../Resource/XMLFile.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+Texture3D::Texture3D(Context* context) :
+    Texture(context)
+{
+}
+
+Texture3D::~Texture3D()
+{
+    Release();
+}
+
+void Texture3D::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Texture3D>();
+}
+
+bool Texture3D::BeginLoad(Deserializer& source)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    String texPath, texName, texExt;
+    SplitPath(GetName(), texPath, texName, texExt);
+    
+    cache->ResetDependencies(this);
+
+    loadParameters_ = new XMLFile(context_);
+    if (!loadParameters_->Load(source))
+    {
+        loadParameters_.Reset();
+        return false;
+    }
+    
+    XMLElement textureElem = loadParameters_->GetRoot();
+    XMLElement volumeElem = textureElem.GetChild("volume");
+    XMLElement colorlutElem = textureElem.GetChild("colorlut");
+
+    if (volumeElem)
+    {
+        String name = volumeElem.GetAttribute("name");
+        
+        String volumeTexPath, volumeTexName, volumeTexExt;
+        SplitPath(name, volumeTexPath, volumeTexName, volumeTexExt);
+        // If path is empty, add the XML file path
+        if (volumeTexPath.Empty())
+            name = texPath + name;
+
+        loadImage_ = cache->GetTempResource<Image>(name);
+        // Precalculate mip levels if async loading
+        if (loadImage_ && GetAsyncLoadState() == ASYNC_LOADING)
+            loadImage_->PrecalculateLevels();
+        cache->StoreResourceDependency(this, name);
+        return true;
+    }
+    else if (colorlutElem)
+    {
+        String name = colorlutElem.GetAttribute("name");
+        
+        String colorlutTexPath, colorlutTexName, colorlutTexExt;
+        SplitPath(name, colorlutTexPath, colorlutTexName, colorlutTexExt);
+        // If path is empty, add the XML file path
+        if (colorlutTexPath.Empty())
+            name = texPath + name;
+
+        SharedPtr<File> file = GetSubsystem<ResourceCache>()->GetFile(name);
+        loadImage_ = new Image(context_);
+        if (!loadImage_->LoadColorLUT(*(file.Get())))
+        {
+            loadParameters_.Reset();
+            loadImage_.Reset();
+            return false;
+        }
+        // Precalculate mip levels if async loading
+        if (loadImage_ && GetAsyncLoadState() == ASYNC_LOADING)
+            loadImage_->PrecalculateLevels();
+        cache->StoreResourceDependency(this, name);
+        return true;
+    }
+
+    LOGERROR("Texture3D XML data for " + GetName() + " did not contain either volume or colorlut element");
+    return false;
+}
+
+bool Texture3D::EndLoad()
+{
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    // If over the texture budget, see if materials can be freed to allow textures to be freed
+    CheckTextureBudget(GetTypeStatic());
+
+    SetParameters(loadParameters_);
+    bool success = SetData(loadImage_);
+    
+    loadImage_.Reset();
+    loadParameters_.Reset();
+    
+    return success;
+}
+
+void Texture3D::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        {
+            if (graphics_->GetTexture(i) == this)
+                graphics_->SetTexture(i, 0);
+        }
+        
+        if (renderSurface_)
+            renderSurface_->Release();
+        
+        ((ID3D11Resource*)object_)->Release();
+        object_ = 0;
+
+        if (shaderResourceView_)
+        {
+            ((ID3D11ShaderResourceView*)shaderResourceView_)->Release();
+            shaderResourceView_ = 0;
+        }
+
+        if (sampler_)
+        {
+            ((ID3D11SamplerState*)sampler_)->Release();
+            sampler_ = 0;
+        }
+    }
+    else
+    {
+        if (renderSurface_)
+            renderSurface_->Release();
+    }
+}
+
+bool Texture3D::SetSize(int width, int height, int depth, unsigned format, TextureUsage usage)
+{
+    // Delete the old rendersurface if any
+    renderSurface_.Reset();
+    usage_ = usage;
+
+    if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        renderSurface_ = new RenderSurface(this);
+
+        // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
+        addressMode_[COORD_U] = ADDRESS_CLAMP;
+        addressMode_[COORD_V] = ADDRESS_CLAMP;
+        filterMode_ = FILTER_NEAREST;
+        requestedLevels_ = 1;
+    }
+    
+    if (usage_ == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(Texture3D, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+
+    width_ = width;
+    height_ = height;
+    depth_ = depth;
+    format_ = format;
+    
+    return Create();
+}
+
+bool Texture3D::SetData(unsigned level, int x, int y, int z, int width, int height, int depth, const void* data)
+{
+    PROFILE(SetTextureData);
+    
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not set data");
+        return false;
+    }
+    
+    if (!data)
+    {
+        LOGERROR("Null source for setting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for setting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    int levelDepth = GetLevelDepth(level);
+    if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || z < 0 || z + depth > levelDepth || width <= 0 || height <= 0 || depth <= 0)
+    {
+        LOGERROR("Illegal dimensions for setting data");
+        return false;
+    }
+    
+    // If compressed, align the update region on a block
+    if (IsCompressed())
+    {
+        x &= ~3;
+        y &= ~3;
+        width += 3;
+        width &= 0xfffffffc;
+        height += 3;
+        height &= 0xfffffffc;
+    }
+
+    unsigned char* src = (unsigned char*)data;
+    unsigned rowSize = GetRowDataSize(width);
+    unsigned rowStart = GetRowDataSize(x);
+    unsigned subResource = D3D11CalcSubresource(level, 0, levels_);
+
+    if (usage_ == TEXTURE_DYNAMIC)
+    {
+        if (IsCompressed())
+        {
+            height = (height + 3) >> 2;
+            y >>= 2;
+        }
+
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)object_, subResource, D3D11_MAP_WRITE_DISCARD, 0,
+            &mappedData);
+        if (mappedData.pData)
+        {
+            for (int page = 0; page < depth; ++page)
+            {
+                for (int row = 0; row < height; ++row)
+                {
+                    memcpy((unsigned char*)mappedData.pData + (page + z) * mappedData.DepthPitch + (row + y) * mappedData.RowPitch
+                        + rowStart, src + row * rowSize, rowSize);
+                }
+            }
+
+            graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)object_, subResource);
+        }
+        else
+        {
+            LOGERROR("Failed to map texture for update");
+            return false;
+        }
+    }
+    else
+    {
+        if (IsCompressed())
+            levelHeight = (levelHeight + 3) >> 2;
+
+        D3D11_BOX destBox;
+        destBox.left = x;
+        destBox.right = x + width;
+        destBox.top = y;
+        destBox.bottom = y + height;
+        destBox.front = z;
+        destBox.back = z + depth;
+
+        graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Resource*)object_, subResource, &destBox, data,
+            rowSize, levelHeight * rowSize);
+    }
+
+    return true;
+}
+
+bool Texture3D::SetData(SharedPtr<Image> image, bool useAlpha)
+{
+    if (!image)
+    {
+        LOGERROR("Null image, can not load texture");
+        return false;
+    }
+    
+    unsigned memoryUse = sizeof(Texture3D);
+    
+    int quality = QUALITY_HIGH;
+    Renderer* renderer = GetSubsystem<Renderer>();
+    if (renderer)
+        quality = renderer->GetTextureQuality();
+    
+    if (!image->IsCompressed())
+    {
+        // Convert unsuitable formats to RGBA
+        unsigned components = image->GetComponents();
+        if ((components == 1 && !useAlpha) || components == 2 || components == 3)
+        {
+            image = image->ConvertToRGBA();
+            if (!image)
+                return false;
+            components = image->GetComponents();
+        }
+
+        unsigned char* levelData = image->GetData();
+        int levelWidth = image->GetWidth();
+        int levelHeight = image->GetHeight();
+        int levelDepth = image->GetDepth();
+        unsigned format = 0;
+        
+        // Discard unnecessary mip levels
+        for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
+        {
+            image = image->GetNextLevel();
+            levelData = image->GetData();
+            levelWidth = image->GetWidth();
+            levelHeight = image->GetHeight();
+            levelDepth = image->GetDepth();
+        }
+        
+        switch (components)
+        {
+        case 1:
+            format = Graphics::GetAlphaFormat();
+            break;
+
+        case 4:
+            format = Graphics::GetRGBAFormat();
+            break;
+        }
+        
+        // If image was previously compressed, reset number of requested levels to avoid error if level count is too high for new size
+        if (IsCompressed() && requestedLevels_ > 1)
+            requestedLevels_ = 0;
+        SetSize(levelWidth, levelHeight, levelDepth, format);
+        
+        for (unsigned i = 0; i < levels_; ++i)
+        {
+            SetData(i, 0, 0, 0, levelWidth, levelHeight, levelDepth, levelData);
+            memoryUse += levelWidth * levelHeight * levelDepth * components;
+            
+            if (i < levels_ - 1)
+            {
+                image = image->GetNextLevel();
+                levelData = image->GetData();
+                levelWidth = image->GetWidth();
+                levelHeight = image->GetHeight();
+                levelDepth = image->GetDepth();
+            }
+        }
+    }
+    else
+    {
+        int width = image->GetWidth();
+        int height = image->GetHeight();
+        int depth = image->GetDepth();
+        unsigned levels = image->GetNumCompressedLevels();
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
+        bool needDecompress = false;
+        
+        if (!format)
+        {
+            format = Graphics::GetRGBAFormat();
+            needDecompress = true;
+        }
+        
+        unsigned mipsToSkip = mipsToSkip_[quality];
+        if (mipsToSkip >= levels)
+            mipsToSkip = levels - 1;
+        while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4 || depth / (1 << mipsToSkip) < 4))
+            --mipsToSkip;
+        width /= (1 << mipsToSkip);
+        height /= (1 << mipsToSkip);
+        depth /= (1 << mipsToSkip);
+        
+        SetNumLevels(Max((int)(levels - mipsToSkip), 1));
+        SetSize(width, height, depth, format);
+        
+        for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
+        {
+            CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
+            if (!needDecompress)
+            {
+                SetData(i, 0, 0, 0, level.width_, level.height_, level.depth_, level.data_);
+                memoryUse += level.depth_ * level.rows_ * level.rowSize_;
+            }
+            else
+            {
+                unsigned char* rgbaData = new unsigned char[level.width_ * level.height_ * level.depth_ * 4];
+                level.Decompress(rgbaData);
+                SetData(i, 0, 0, 0, level.width_, level.height_, level.depth_, rgbaData);
+                memoryUse += level.width_ * level.height_ * level.depth_ * 4;
+                delete[] rgbaData;
+            }
+        }
+    }
+    
+    SetMemoryUse(memoryUse);
+    return true;
+}
+
+bool Texture3D::GetData(unsigned level, void* dest) const
+{
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not get data");
+        return false;
+    }
+    
+    if (!dest)
+    {
+        LOGERROR("Null destination for getting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for getting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    int levelDepth = GetLevelDepth(level);
+    
+    D3D11_TEXTURE3D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = levelWidth;
+    textureDesc.Height = levelHeight;
+    textureDesc.Depth = levelDepth;
+    textureDesc.MipLevels = 1;
+    textureDesc.Format = (DXGI_FORMAT)format_;
+    textureDesc.Usage = D3D11_USAGE_STAGING;
+    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+
+    ID3D11Texture3D* stagingTexture = 0;
+    graphics_->GetImpl()->GetDevice()->CreateTexture3D(&textureDesc, 0, &stagingTexture);
+    if (!stagingTexture)
+    {
+        LOGERROR("Failed to create staging texture for GetData");
+        return false;
+    }
+
+    unsigned srcSubResource = D3D11CalcSubresource(level, 0, levels_);
+    D3D11_BOX srcBox;
+    srcBox.left = 0;
+    srcBox.right = levelWidth;
+    srcBox.top = 0;
+    srcBox.bottom = levelHeight;
+    srcBox.front = 0;
+    srcBox.back = levelDepth;
+    graphics_->GetImpl()->GetDeviceContext()->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, (ID3D11Resource*)object_,
+        srcSubResource, &srcBox);
+
+    D3D11_MAPPED_SUBRESOURCE mappedData;
+    mappedData.pData = 0;
+    unsigned rowSize = GetRowDataSize(levelWidth);
+    unsigned numRows = IsCompressed() ? (levelHeight + 3) >> 2 : levelHeight;
+
+    graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)stagingTexture, 0, D3D11_MAP_READ, 0, &mappedData);
+    if (mappedData.pData)
+    {
+        for (int page = 0; page < levelDepth; ++page)
+        {
+            for (unsigned row = 0; row < numRows; ++row)
+            {
+                memcpy((unsigned char*)dest + (page * numRows + row) * rowSize, (unsigned char*)mappedData.pData + page *
+                    mappedData.DepthPitch + row * mappedData.RowPitch, rowSize);
+            }
+        }
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
+        stagingTexture->Release();
+        return true;
+    }
+    else
+    {
+        LOGERROR("Failed to map staging texture for GetData");
+        stagingTexture->Release();
+        return false;
+    }
+}
+
+bool Texture3D::Create()
+{
+    Release();
+    
+    if (!graphics_ || !width_ || !height_ || !depth_)
+        return false;
+    
+    levels_ = CheckMaxLevels(width_, height_, depth_, requestedLevels_);
+
+    D3D11_TEXTURE3D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = width_;
+    textureDesc.Height = height_;
+    textureDesc.Depth = depth_;
+    textureDesc.MipLevels = levels_;
+    textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
+    textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    textureDesc.CPUAccessFlags = usage_ == TEXTURE_DYNAMIC ? D3D11_CPU_ACCESS_WRITE : 0;
+
+    graphics_->GetImpl()->GetDevice()->CreateTexture3D(&textureDesc, 0, (ID3D11Texture3D**)&object_);
+    if (!object_)
+    {
+        LOGERROR("Failed to create texture");
+        return false;
+    }
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
+    memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
+    resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
+    resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+    resourceViewDesc.Texture3D.MipLevels = (unsigned)levels_;
+
+    graphics_->GetImpl()->GetDevice()->CreateShaderResourceView((ID3D11Resource*)object_, &resourceViewDesc,
+        (ID3D11ShaderResourceView**)&shaderResourceView_);
+    if (!shaderResourceView_)
+    {
+        LOGERROR("Failed to create shader resource view for texture");
+        return false;
+    }
+
+    return true;
+}
+
+void Texture3D::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    if (renderSurface_ && renderSurface_->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+        renderSurface_->QueueUpdate();
+}
+
+}

+ 80 - 0
Source/Atomic/Graphics/Direct3D11/D3D11Texture3D.h

@@ -0,0 +1,80 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/RenderSurface.h"
+#include "../../Container/Ptr.h"
+#include "../../Graphics/Texture.h"
+
+namespace Urho3D
+{
+
+class Image;
+
+/// 3D texture resource.
+class URHO3D_API Texture3D : public Texture
+{
+    OBJECT(Texture3D);
+    
+public:
+    /// Construct.
+    Texture3D(Context* context);
+    /// Destruct.
+    virtual ~Texture3D();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Load resource from stream. May be called from a worker thread. Return true if successful.
+    virtual bool BeginLoad(Deserializer& source);
+    /// Finish resource loading. Always called from the main thread. Return true if successful.
+    virtual bool EndLoad();
+    /// Release texture.
+    virtual void Release();
+    
+    /// Set size, format and usage. Zero size will follow application window size. Return true if successful.
+    bool SetSize(int width, int height, int depth, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    /// Set data either partially or fully on a mip level. Return true if successful.
+    bool SetData(unsigned level, int x, int y, int z, int width, int height, int depth, const void* data);
+    /// Set data from an image. Return true if successful. Optionally make a single channel image alpha-only.
+    bool SetData(SharedPtr<Image> image, bool useAlpha = false);
+    
+    /// Get data from a mip level. The destination buffer must be big enough. Return true if successful.
+    bool GetData(unsigned level, void* dest) const;
+    /// Return render surface.
+    RenderSurface* GetRenderSurface() const { return renderSurface_; }
+    
+private:
+    /// Create texture.
+    bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
+    
+    /// Render surface.
+    SharedPtr<RenderSurface> renderSurface_;
+    /// Image file acquired during BeginLoad.
+    SharedPtr<Image> loadImage_;
+    /// Parameter file acquired during BeginLoad.
+    SharedPtr<XMLFile> loadParameters_;
+};
+
+}

+ 739 - 0
Source/Atomic/Graphics/Direct3D11/D3D11TextureCube.cpp

@@ -0,0 +1,739 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/Context.h"
+#include "../../IO/FileSystem.h"
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsEvents.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Core/Profiler.h"
+#include "../../Graphics/Renderer.h"
+#include "../../Resource/ResourceCache.h"
+#include "../../Graphics/TextureCube.h"
+#include "../../Resource/XMLFile.h"
+
+#include "../../DebugNew.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355)
+#endif
+
+namespace Urho3D
+{
+
+static const char* cubeMapLayoutNames[] = {
+    "horizontal",
+    "horizontalnvidia",
+    "horizontalcross",
+    "verticalcross",
+    "blender",
+    0
+};
+
+static SharedPtr<Image> GetTileImage(Image* src, int tileX, int tileY, int tileWidth, int tileHeight)
+{
+    return SharedPtr<Image>(src->GetSubimage(IntRect(tileX * tileWidth, tileY * tileHeight, (tileX + 1) * tileWidth, (tileY + 1) * tileHeight)));
+}
+
+TextureCube::TextureCube(Context* context) :
+    Texture(context),
+    lockedLevel_(-1)
+{
+    // Default to clamp mode addressing
+    addressMode_[COORD_U] = ADDRESS_CLAMP;
+    addressMode_[COORD_V] = ADDRESS_CLAMP;
+    addressMode_[COORD_W] = ADDRESS_CLAMP;
+    
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        faceMemoryUse_[i] = 0;
+}
+
+TextureCube::~TextureCube()
+{
+    Release();
+}
+
+void TextureCube::RegisterObject(Context* context)
+{
+    context->RegisterFactory<TextureCube>();
+}
+
+bool TextureCube::BeginLoad(Deserializer& source)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    cache->ResetDependencies(this);
+
+    String texPath, texName, texExt;
+    SplitPath(GetName(), texPath, texName, texExt);
+    
+    loadParameters_ = (new XMLFile(context_));
+    if (!loadParameters_->Load(source))
+    {
+        loadParameters_.Reset();
+        return false;
+    }
+    
+    loadImages_.Clear();
+
+    XMLElement textureElem = loadParameters_->GetRoot();
+    XMLElement imageElem = textureElem.GetChild("image");
+    // Single image and multiple faces with layout
+    if (imageElem)
+    {
+        String name = imageElem.GetAttribute("name");
+        // If path is empty, add the XML file path
+        if (GetPath(name).Empty())
+            name = texPath + name;
+        
+        CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
+        SharedPtr<Image> image = cache->GetTempResource<Image>(name);
+        if (!image)
+            return false;
+        
+        int faceWidth, faceHeight;
+        loadImages_.Resize(MAX_CUBEMAP_FACES);
+        
+        switch (layout)
+        {
+        case CML_HORIZONTAL:
+            faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
+            faceHeight = image->GetHeight();
+            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 3, 0, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 4, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 5, 0, faceWidth, faceHeight);
+            break;
+            
+        case CML_HORIZONTALNVIDIA:
+            faceWidth = image->GetWidth() / MAX_CUBEMAP_FACES;
+            faceHeight = image->GetHeight();
+            for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+                loadImages_[i] = GetTileImage(image, i, 0, faceWidth, faceHeight);
+            break;
+            
+        case CML_HORIZONTALCROSS:
+            faceWidth = image->GetWidth() / 4;
+            faceHeight = image->GetHeight() / 3;
+            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 3, 1, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
+            break;
+            
+        case CML_VERTICALCROSS:
+            faceWidth = image->GetWidth() / 3;
+            faceHeight = image->GetHeight() / 4;
+            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 1, 2, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 3, faceWidth, faceHeight);
+            if (loadImages_[FACE_NEGATIVE_Z])
+            {
+                loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
+                loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
+            }
+            break;
+            
+        case CML_BLENDER:
+            faceWidth = image->GetWidth() / 3;
+            faceHeight = image->GetHeight() / 2;
+            loadImages_[FACE_NEGATIVE_X] = GetTileImage(image, 0, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Z] = GetTileImage(image, 1, 0, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_X] = GetTileImage(image, 2, 0, faceWidth, faceHeight);
+            loadImages_[FACE_NEGATIVE_Y] = GetTileImage(image, 0, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Y] = GetTileImage(image, 1, 1, faceWidth, faceHeight);
+            loadImages_[FACE_POSITIVE_Z] = GetTileImage(image, 2, 1, faceWidth, faceHeight);
+            break;
+        }
+    }
+    // Face per image
+    else
+    {
+        XMLElement faceElem = textureElem.GetChild("face");
+        while (faceElem)
+        {
+            String name = faceElem.GetAttribute("name");
+            
+            // If path is empty, add the XML file path
+            if (GetPath(name).Empty())
+                name = texPath + name;
+            
+            loadImages_.Push(cache->GetTempResource<Image>(name));
+            cache->StoreResourceDependency(this, name);
+            
+            faceElem = faceElem.GetNext("face");
+        }
+    }
+
+    // Precalculate mip levels if async loading
+    if (GetAsyncLoadState() == ASYNC_LOADING)
+    {
+        for (unsigned i = 0; i < loadImages_.Size(); ++i)
+        {
+            if (loadImages_[i])
+                loadImages_[i]->PrecalculateLevels();
+        }
+    }
+
+    return true;
+}
+
+bool TextureCube::EndLoad()
+{
+    // In headless mode, do not actually load the texture, just return success
+    if (!graphics_)
+        return true;
+    
+    // If over the texture budget, see if materials can be freed to allow textures to be freed
+    CheckTextureBudget(GetTypeStatic());
+
+    SetParameters(loadParameters_);
+    
+    for (unsigned i = 0; i < loadImages_.Size() && i < MAX_CUBEMAP_FACES; ++i)
+        SetData((CubeMapFace)i, loadImages_[i]);
+    
+    loadImages_.Clear();
+    loadParameters_.Reset();
+    
+    return true;
+}
+
+void TextureCube::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i)
+        {
+            if (graphics_->GetTexture(i) == this)
+                graphics_->SetTexture(i, 0);
+        }
+        
+        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        {
+            if (renderSurfaces_[i])
+                renderSurfaces_[i]->Release();
+        }
+        
+        ((ID3D11Resource*)object_)->Release();
+        object_ = 0;
+
+        if (shaderResourceView_)
+        {
+            ((ID3D11ShaderResourceView*)shaderResourceView_)->Release();
+            shaderResourceView_ = 0;
+        }
+
+        if (sampler_)
+        {
+            ((ID3D11SamplerState*)sampler_)->Release();
+            sampler_ = 0;
+        }
+    }
+}
+
+bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage)
+{
+    if (size <= 0)
+    {
+        LOGERROR("Zero or negative cube texture size");
+        return false;
+    }
+    if (usage == TEXTURE_DEPTHSTENCIL)
+    {
+        LOGERROR("Depth-stencil usage not supported for cube maps");
+        return false;
+    }
+    
+    // Delete the old rendersurfaces if any
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        renderSurfaces_[i].Reset();
+        faceMemoryUse_[i] = 0;
+    }
+    
+    usage_ = usage;
+    if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+            renderSurfaces_[i] = new RenderSurface(this);
+        
+        // Nearest filtering and mipmaps disabled by default
+        filterMode_ = FILTER_NEAREST;
+        requestedLevels_ = 1;
+    }
+    
+    if (usage_ == TEXTURE_RENDERTARGET)
+        SubscribeToEvent(E_RENDERSURFACEUPDATE, HANDLER(TextureCube, HandleRenderSurfaceUpdate));
+    else
+        UnsubscribeFromEvent(E_RENDERSURFACEUPDATE);
+    
+    width_ = size;
+    height_ = size;
+    format_ = format;
+    
+    return Create();
+}
+
+bool TextureCube::SetData(CubeMapFace face, unsigned level, int x, int y, int width, int height, const void* data)
+{
+    PROFILE(SetTextureData);
+    
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not set data");
+        return false;
+    }
+    
+    if (!data)
+    {
+        LOGERROR("Null source for setting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for setting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+    if (x < 0 || x + width > levelWidth || y < 0 || y + height > levelHeight || width <= 0 || height <= 0)
+    {
+        LOGERROR("Illegal dimensions for setting data");
+        return false;
+    }
+
+    // If compressed, align the update region on a block
+    if (IsCompressed())
+    {
+        x &= ~3;
+        y &= ~3;
+        width += 3;
+        width &= 0xfffffffc;
+        height += 3;
+        height &= 0xfffffffc;
+    }
+
+    unsigned char* src = (unsigned char*)data;
+    unsigned rowSize = GetRowDataSize(width);
+    unsigned rowStart = GetRowDataSize(x);
+    unsigned subResource = D3D11CalcSubresource(level, face, levels_);
+
+    if (usage_ == TEXTURE_DYNAMIC)
+    {
+        if (IsCompressed())
+        {
+            height = (height + 3) >> 2;
+            y >>= 2;
+        }
+
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)object_, subResource, D3D11_MAP_WRITE_DISCARD, 0,
+            &mappedData);
+        if (mappedData.pData)
+        {
+            for (int row = 0; row < height; ++row)
+                memcpy((unsigned char*)mappedData.pData + (row + y) * mappedData.RowPitch + rowStart, src + row * rowSize, rowSize);
+            graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)object_, subResource);
+        }
+        else
+        {
+            LOGERROR("Failed to map texture for update");
+            return false;
+        }
+    }
+    else
+    {
+        D3D11_BOX destBox;
+        destBox.left = x;
+        destBox.right = x + width;
+        destBox.top = y;
+        destBox.bottom = y + height;
+        destBox.front = 0;
+        destBox.back = 1;
+
+        graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Resource*)object_, subResource, &destBox, data,
+            rowSize, 0);
+    }
+
+    return true;
+}
+
+bool TextureCube::SetData(CubeMapFace face, Deserializer& source)
+{
+    SharedPtr<Image> image(new Image(context_));
+    if (!image->Load(source))
+        return false;
+    
+    return SetData(face, image);
+}
+
+bool TextureCube::SetData(CubeMapFace face, SharedPtr<Image> image, bool useAlpha)
+{
+    if (!image)
+    {
+        LOGERROR("Null image, can not load texture");
+        return false;
+    }
+    
+    unsigned memoryUse = 0;
+    
+    int quality = QUALITY_HIGH;
+    Renderer* renderer = GetSubsystem<Renderer>();
+    if (renderer)
+        quality = renderer->GetTextureQuality();
+    
+    if (!image->IsCompressed())
+    {
+        // Convert unsuitable formats to RGBA
+        unsigned components = image->GetComponents();
+        if ((components == 1 && !useAlpha) || components == 2 || components == 3)
+        {
+            image = image->ConvertToRGBA();
+            if (!image)
+                return false;
+            components = image->GetComponents();
+        }
+
+        unsigned char* levelData = image->GetData();
+        int levelWidth = image->GetWidth();
+        int levelHeight = image->GetHeight();
+        unsigned format = 0;
+        
+        if (levelWidth != levelHeight)
+        {
+            LOGERROR("Cube texture width not equal to height");
+            return false;
+        }
+
+        // Discard unnecessary mip levels
+        for (unsigned i = 0; i < mipsToSkip_[quality]; ++i)
+        {
+            image = image->GetNextLevel();
+            levelData = image->GetData();
+            levelWidth = image->GetWidth();
+            levelHeight = image->GetHeight();
+        }
+        
+        switch (components)
+        {
+        case 1:
+            format = Graphics::GetAlphaFormat();
+            break;
+
+        case 4:
+            format = Graphics::GetRGBAFormat();
+            break;
+        }
+        
+        // Create the texture when face 0 is being loaded, check that rest of the faces are same size & format
+        if (!face)
+        {
+            // If image was previously compressed, reset number of requested levels to avoid error if level count is too high for new size
+            if (IsCompressed() && requestedLevels_ > 1)
+                requestedLevels_ = 0;
+            SetSize(levelWidth, format);
+        }
+        else
+        {
+            if (!object_)
+            {
+                LOGERROR("Cube texture face 0 must be loaded first");
+                return false;
+            }
+            if (levelWidth != width_ || format != format_)
+            {
+                LOGERROR("Cube texture face does not match size or format of face 0");
+                return false;
+            }
+        }
+        
+        for (unsigned i = 0; i < levels_; ++i)
+        {
+            SetData(face, i, 0, 0, levelWidth, levelHeight, levelData);
+            memoryUse += levelWidth * levelHeight * components;
+            
+            if (i < levels_ - 1)
+            {
+                image = image->GetNextLevel();
+                levelData = image->GetData();
+                levelWidth = image->GetWidth();
+                levelHeight = image->GetHeight();
+            }
+        }
+    }
+    else
+    {
+        int width = image->GetWidth();
+        int height = image->GetHeight();
+        unsigned levels = image->GetNumCompressedLevels();
+        unsigned format = graphics_->GetFormat(image->GetCompressedFormat());
+        bool needDecompress = false;
+        
+        if (width != height)
+        {
+            LOGERROR("Cube texture width not equal to height");
+            return false;
+        }
+        
+        if (!format)
+        {
+            format = Graphics::GetRGBAFormat();
+            needDecompress = true;
+        }
+        
+        unsigned mipsToSkip = mipsToSkip_[quality];
+        if (mipsToSkip >= levels)
+            mipsToSkip = levels - 1;
+        while (mipsToSkip && (width / (1 << mipsToSkip) < 4 || height / (1 << mipsToSkip) < 4))
+            --mipsToSkip;
+        width /= (1 << mipsToSkip);
+        height /= (1 << mipsToSkip);
+        
+        // Create the texture when face 0 is being loaded, assume rest of the faces are same size & format
+        if (!face)
+        {
+            SetNumLevels(Max((int)(levels - mipsToSkip), 1));
+            SetSize(width, format);
+        }
+        else
+        {
+            if (!object_)
+            {
+                LOGERROR("Cube texture face 0 must be loaded first");
+                return false;
+            }
+            if (width != width_ || format != format_)
+            {
+                LOGERROR("Cube texture face does not match size or format of face 0");
+                return false;
+            }
+        }
+        
+        for (unsigned i = 0; i < levels_ && i < levels - mipsToSkip; ++i)
+        {
+            CompressedLevel level = image->GetCompressedLevel(i + mipsToSkip);
+            if (!needDecompress)
+            {
+                SetData(face, i, 0, 0, level.width_, level.height_, level.data_);
+                memoryUse += level.rows_ * level.rowSize_;
+            }
+            else
+            {
+                unsigned char* rgbaData = new unsigned char[level.width_ * level.height_ * 4];
+                level.Decompress(rgbaData);
+                SetData(face, i, 0, 0, level.width_, level.height_, rgbaData);
+                memoryUse += level.width_ * level.height_ * 4;
+                delete[] rgbaData;
+            }
+        }
+    }
+    
+    faceMemoryUse_[face] = memoryUse;
+    unsigned totalMemoryUse = sizeof(TextureCube);
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        totalMemoryUse += faceMemoryUse_[i];
+    SetMemoryUse(totalMemoryUse);
+    
+    return true;
+}
+
+bool TextureCube::GetData(CubeMapFace face, unsigned level, void* dest) const
+{
+    if (!object_)
+    {
+        LOGERROR("No texture created, can not get data");
+        return false;
+    }
+    
+    if (!dest)
+    {
+        LOGERROR("Null destination for getting data");
+        return false;
+    }
+    
+    if (level >= levels_)
+    {
+        LOGERROR("Illegal mip level for getting data");
+        return false;
+    }
+    
+    int levelWidth = GetLevelWidth(level);
+    int levelHeight = GetLevelHeight(level);
+
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = levelWidth;
+    textureDesc.Height = levelHeight;
+    textureDesc.MipLevels = 1;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = (DXGI_FORMAT)format_;
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = D3D11_USAGE_STAGING;
+    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+
+    ID3D11Texture2D* stagingTexture = 0;
+    graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, &stagingTexture);
+    if (!stagingTexture)
+    {
+        LOGERROR("Failed to create staging texture for GetData");
+        return false;
+    }
+
+    unsigned srcSubResource = D3D11CalcSubresource(level, face, levels_);
+    D3D11_BOX srcBox;
+    srcBox.left = 0;
+    srcBox.right = levelWidth;
+    srcBox.top = 0;
+    srcBox.bottom = levelHeight;
+    srcBox.front = 0;
+    srcBox.back = 1;
+    graphics_->GetImpl()->GetDeviceContext()->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, (ID3D11Resource*)object_,
+        srcSubResource, &srcBox);
+
+    D3D11_MAPPED_SUBRESOURCE mappedData;
+    mappedData.pData = 0;
+    unsigned rowSize = GetRowDataSize(levelWidth);
+    unsigned numRows = IsCompressed() ? (levelHeight + 3) >> 2 : levelHeight;
+
+    graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Resource*)stagingTexture, 0, D3D11_MAP_READ, 0, &mappedData);
+    if (mappedData.pData)
+    {
+        for (unsigned row = 0; row < numRows; ++row)
+            memcpy((unsigned char*)dest + row * rowSize, (unsigned char*)mappedData.pData + row * mappedData.RowPitch, rowSize);
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Resource*)stagingTexture, 0);
+        stagingTexture->Release();
+        return true;
+    }
+    else
+    {
+        LOGERROR("Failed to map staging texture for GetData");
+        stagingTexture->Release();
+        return false;
+    }
+}
+
+bool TextureCube::Create()
+{
+    Release();
+    
+    if (!graphics_ || !width_ || !height_)
+        return false;
+    
+    levels_ = CheckMaxLevels(width_, height_, requestedLevels_);
+
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = width_;
+    textureDesc.Height = height_;
+    textureDesc.MipLevels = levels_;
+    textureDesc.ArraySize = MAX_CUBEMAP_FACES;
+    textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Usage = usage_ == TEXTURE_DYNAMIC ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    if (usage_ == TEXTURE_RENDERTARGET)
+        textureDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
+    else if (usage_ == TEXTURE_DEPTHSTENCIL)
+        textureDesc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
+    textureDesc.CPUAccessFlags = usage_ == TEXTURE_DYNAMIC ? D3D11_CPU_ACCESS_WRITE : 0;
+    textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+
+    graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_);
+    if (!object_)
+    {
+        LOGERROR("Failed to create texture");
+        return false;
+    }
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
+    memset(&resourceViewDesc, 0, sizeof resourceViewDesc);
+    resourceViewDesc.Format = (DXGI_FORMAT)GetSRVFormat(textureDesc.Format);
+    resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+    resourceViewDesc.Texture2D.MipLevels = (unsigned)levels_;
+
+    graphics_->GetImpl()->GetDevice()->CreateShaderResourceView((ID3D11Resource*)object_, &resourceViewDesc,
+        (ID3D11ShaderResourceView**)&shaderResourceView_);
+    if (!shaderResourceView_)
+    {
+        LOGERROR("Failed to create shader resource view for texture");
+        return false;
+    }
+
+    if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+        {
+            renderSurfaces_[i] = new RenderSurface(this);
+
+            D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
+            memset(&renderTargetViewDesc, 0, sizeof renderTargetViewDesc);
+            renderTargetViewDesc.Format = textureDesc.Format;
+            renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+            renderTargetViewDesc.Texture2DArray.ArraySize = 1;
+            renderTargetViewDesc.Texture2DArray.FirstArraySlice = i;
+            renderTargetViewDesc.Texture2DArray.MipSlice = 0;
+
+            graphics_->GetImpl()->GetDevice()->CreateRenderTargetView((ID3D11Resource*)object_, &renderTargetViewDesc,
+                (ID3D11RenderTargetView**)&renderSurfaces_[i]->renderTargetView_);
+
+            if (!renderSurfaces_[i]->renderTargetView_)
+            {
+                LOGERROR("Failed to create rendertarget view for texture");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+void TextureCube::HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData)
+{
+    for (unsigned i = 0; i < MAX_CUBEMAP_FACES; ++i)
+    {
+        if (renderSurfaces_[i] && renderSurfaces_[i]->GetUpdateMode() == SURFACE_UPDATEALWAYS)
+            renderSurfaces_[i]->QueueUpdate();
+    }
+}
+
+}

+ 89 - 0
Source/Atomic/Graphics/Direct3D11/D3D11TextureCube.h

@@ -0,0 +1,89 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/RenderSurface.h"
+#include "../../Container/Ptr.h"
+#include "../../Graphics/Texture.h"
+
+namespace Urho3D
+{
+
+class Deserializer;
+class Image;
+
+/// Cube texture resource.
+class URHO3D_API TextureCube : public Texture
+{
+    OBJECT(TextureCube);
+    
+public:
+    /// Construct.
+    TextureCube(Context* context);
+    /// Destruct.
+    virtual ~TextureCube();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Load resource from stream. May be called from a worker thread. Return true if successful.
+    virtual bool BeginLoad(Deserializer& source);
+    /// Finish resource loading. Always called from the main thread. Return true if successful.
+    virtual bool EndLoad();
+    /// Release texture.
+    virtual void Release();
+    
+    /// Set size, format and usage. Return true if successful.
+    bool SetSize(int size, unsigned format, TextureUsage usage = TEXTURE_STATIC);
+    /// Set data either partially or fully on a face's mip level. Return true if successful.
+    bool SetData(CubeMapFace face, unsigned level, int x, int y, int width, int height, const void* data);
+    /// Set data of one face from a stream. Return true if successful.
+    bool SetData(CubeMapFace face, Deserializer& source);
+    /// Set data of one face from an image. Return true if successful. Optionally make a single channel image alpha-only.
+    bool SetData(CubeMapFace face, SharedPtr<Image> image, bool useAlpha = false);
+    
+    /// Get data from a face's mip level. The destination buffer must be big enough. Return true if successful.
+    bool GetData(CubeMapFace face, unsigned level, void* dest) const;
+    /// Return render surface for one face.
+    RenderSurface* GetRenderSurface(CubeMapFace face) const { return renderSurfaces_[face]; }
+    
+private:
+    /// Create texture.
+    bool Create();
+    /// Handle render surface update event.
+    void HandleRenderSurfaceUpdate(StringHash eventType, VariantMap& eventData);
+    
+    /// Render surfaces.
+    SharedPtr<RenderSurface> renderSurfaces_[MAX_CUBEMAP_FACES];
+    /// Memory use per face.
+    unsigned faceMemoryUse_[MAX_CUBEMAP_FACES];
+    /// Currently locked mip level.
+    int lockedLevel_;
+    /// Currently locked face.
+    CubeMapFace lockedFace_;
+    /// Face image files acquired during BeginLoad.
+    Vector<SharedPtr<Image> > loadImages_;
+    /// Parameter file acquired during BeginLoad.
+    SharedPtr<XMLFile> loadParameters_;
+};
+
+}

+ 479 - 0
Source/Atomic/Graphics/Direct3D11/D3D11VertexBuffer.cpp

@@ -0,0 +1,479 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/VertexBuffer.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+const unsigned VertexBuffer::elementSize[] =
+{
+    3 * sizeof(float), // Position
+    3 * sizeof(float), // Normal
+    4 * sizeof(unsigned char), // Color
+    2 * sizeof(float), // Texcoord1
+    2 * sizeof(float), // Texcoord2
+    3 * sizeof(float), // Cubetexcoord1
+    3 * sizeof(float), // Cubetexcoord2
+    4 * sizeof(float), // Tangent
+    4 * sizeof(float), // Blendweights
+    4 * sizeof(unsigned char), // Blendindices
+    4 * sizeof(float), // Instancematrix1
+    4 * sizeof(float), // Instancematrix2
+    4 * sizeof(float) // Instancematrix3
+};
+
+const char* VertexBuffer::elementSemantics[] =
+{
+    "POSITION",
+    "NORMAL",
+    "COLOR",
+    "TEXCOORD",
+    "TEXCOORD",
+    "TEXCOORD",
+    "TEXCOORD",
+    "TANGENT",
+    "BLENDWEIGHT",
+    "BLENDINDICES",
+    "TEXCOORD",
+    "TEXCOORD",
+    "TEXCOORD"
+};
+
+const unsigned VertexBuffer::elementSemanticIndices[] =
+{
+    0,
+    0,
+    0,
+    0,
+    1,
+    0,
+    1,
+    0,
+    0,
+    0,
+    2,
+    3,
+    4
+};
+
+const unsigned VertexBuffer::elementFormats[] =
+{
+    DXGI_FORMAT_R32G32B32_FLOAT,
+    DXGI_FORMAT_R32G32B32_FLOAT,
+    DXGI_FORMAT_R8G8B8A8_UNORM,
+    DXGI_FORMAT_R32G32_FLOAT,
+    DXGI_FORMAT_R32G32_FLOAT,
+    DXGI_FORMAT_R32G32B32_FLOAT,
+    DXGI_FORMAT_R32G32B32_FLOAT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT,
+    DXGI_FORMAT_R8G8B8A8_UINT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT,
+    DXGI_FORMAT_R32G32B32A32_FLOAT
+};
+
+VertexBuffer::VertexBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>()),
+    vertexCount_(0),
+    elementMask_(0),
+    lockState_(LOCK_NONE),
+    lockStart_(0),
+    lockCount_(0),
+    lockScratchData_(0),
+    dynamic_(false),
+    shadowed_(false)
+{
+    UpdateOffsets();
+    
+    // Force shadowing mode if graphics subsystem does not exist
+    if (!graphics_)
+        shadowed_ = true;
+}
+
+VertexBuffer::~VertexBuffer()
+{
+    Release();
+}
+
+void VertexBuffer::Release()
+{
+    Unlock();
+    
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+        
+        for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+        {
+            if (graphics_->GetVertexBuffer(i) == this)
+                graphics_->SetVertexBuffer(0);
+        }
+        
+        ((ID3D11Buffer*)object_)->Release();
+        object_ = 0;
+    }
+}
+
+void VertexBuffer::SetShadowed(bool enable)
+{
+    // If no graphics subsystem, can not disable shadowing
+    if (!graphics_)
+        enable = true;
+    
+    if (enable != shadowed_)
+    {
+        if (enable && vertexSize_ && vertexCount_)
+            shadowData_ = new unsigned char[vertexCount_ * vertexSize_];
+        else
+            shadowData_.Reset();
+        
+        shadowed_ = enable;
+    }
+}
+
+bool VertexBuffer::SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic)
+{
+    Unlock();
+    
+    dynamic_ = dynamic;
+    vertexCount_ = vertexCount;
+    elementMask_ = elementMask;
+    
+    UpdateOffsets();
+    
+    if (shadowed_ && vertexCount_ && vertexSize_)
+        shadowData_ = new unsigned char[vertexCount_ * vertexSize_];
+    else
+        shadowData_.Reset();
+    
+    return Create();
+}
+
+bool VertexBuffer::SetData(const void* data)
+{
+    if (!data)
+    {
+        LOGERROR("Null pointer for vertex buffer data");
+        return false;
+    }
+    
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not set vertex buffer data");
+        return false;
+    }
+    
+    if (shadowData_ && data != shadowData_.Get())
+        memcpy(shadowData_.Get(), data, vertexCount_ * vertexSize_);
+    
+    if (object_)
+    {
+        if (dynamic_)
+        {
+            void* hwData = MapBuffer(0, vertexCount_, true);
+            if (hwData)
+            {
+                memcpy(hwData, data, vertexCount_ * vertexSize_);
+                UnmapBuffer();
+            }
+            else
+                return false;
+        }
+        else
+        {
+            D3D11_BOX destBox;
+            destBox.left = 0;
+            destBox.right = vertexCount_ * vertexSize_;
+            destBox.top = 0;
+            destBox.bottom = 1;
+            destBox.front = 0;
+            destBox.back = 1;
+
+            graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, &destBox, data, 0, 0);
+        }
+    }
+    
+    return true;
+}
+
+bool VertexBuffer::SetDataRange(const void* data, unsigned start, unsigned count, bool discard)
+{
+    if (start == 0 && count == vertexCount_)
+        return SetData(data);
+    
+    if (!data)
+    {
+        LOGERROR("Null pointer for vertex buffer data");
+        return false;
+    }
+    
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not set vertex buffer data");
+        return false;
+    }
+    
+    if (start + count > vertexCount_)
+    {
+        LOGERROR("Illegal range for setting new vertex buffer data");
+        return false;
+    }
+    
+    if (!count)
+        return true;
+    
+    if (shadowData_ && shadowData_.Get() + start * vertexSize_ != data)
+        memcpy(shadowData_.Get() + start * vertexSize_, data, count * vertexSize_);
+    
+    if (object_)
+    {
+        if (dynamic_)
+        {
+            void* hwData = MapBuffer(start, count, discard);
+            if (hwData)
+            {
+                memcpy(hwData, data, count * vertexSize_);
+                UnmapBuffer();
+            }
+            else
+                return false;
+        }
+        else
+        {
+            D3D11_BOX destBox;
+            destBox.left = start * vertexSize_;
+            destBox.right = destBox.left + count * vertexSize_;
+            destBox.top = 0;
+            destBox.bottom = 1;
+            destBox.front = 0;
+            destBox.back = 1;
+
+            graphics_->GetImpl()->GetDeviceContext()->UpdateSubresource((ID3D11Buffer*)object_, 0, &destBox, data, 0, 0);
+        }
+    }
+
+    return true;
+}
+
+void* VertexBuffer::Lock(unsigned start, unsigned count, bool discard)
+{
+    if (lockState_ != LOCK_NONE)
+    {
+        LOGERROR("Vertex buffer already locked");
+        return 0;
+    }
+    
+    if (!vertexSize_)
+    {
+        LOGERROR("Vertex elements not defined, can not lock vertex buffer");
+        return 0;
+    }
+    
+    if (start + count > vertexCount_)
+    {
+        LOGERROR("Illegal range for locking vertex buffer");
+        return 0;
+    }
+    
+    if (!count)
+        return 0;
+    
+    lockStart_ = start;
+    lockCount_ = count;
+    
+    // Because shadow data must be kept in sync, can only lock hardware buffer if not shadowed
+    if (object_ && !shadowData_ && dynamic_)
+        return MapBuffer(start, count, discard);
+    else if (shadowData_)
+    {
+        lockState_ = LOCK_SHADOW;
+        return shadowData_.Get() + start * vertexSize_;
+    }
+    else if (graphics_)
+    {
+        lockState_ = LOCK_SCRATCH;
+        lockScratchData_ = graphics_->ReserveScratchBuffer(count * vertexSize_);
+        return lockScratchData_;
+    }
+    else
+        return 0;
+}
+
+void VertexBuffer::Unlock()
+{
+    switch (lockState_)
+    {
+    case LOCK_HARDWARE:
+        UnmapBuffer();
+        break;
+        
+    case LOCK_SHADOW:
+        SetDataRange(shadowData_.Get() + lockStart_ * vertexSize_, lockStart_, lockCount_);
+        lockState_ = LOCK_NONE;
+        break;
+        
+    case LOCK_SCRATCH:
+        SetDataRange(lockScratchData_, lockStart_, lockCount_);
+        if (graphics_)
+            graphics_->FreeScratchBuffer(lockScratchData_);
+        lockScratchData_ = 0;
+        lockState_ = LOCK_NONE;
+        break;
+    }
+}
+
+void VertexBuffer::UpdateOffsets()
+{
+    unsigned elementOffset = 0;
+    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    {
+        if (elementMask_ & (1 << i))
+        {
+            elementOffset_[i] = elementOffset;
+            elementOffset += elementSize[i];
+        }
+        else
+            elementOffset_[i] = NO_ELEMENT;
+    }
+    vertexSize_ = elementOffset;
+}
+
+unsigned long long VertexBuffer::GetBufferHash(unsigned streamIndex, unsigned useMask)
+{
+    unsigned long long bufferHash = elementMask_;
+    unsigned long long maskHash;
+    if (useMask == MASK_DEFAULT)
+        maskHash = ((unsigned long long)elementMask_) * 0x100000000ULL;
+    else
+        maskHash = ((unsigned long long)useMask) * 0x100000000ULL;
+    
+    bufferHash |= maskHash;
+    bufferHash <<= streamIndex * MAX_VERTEX_ELEMENTS;
+    
+    return bufferHash;
+}
+
+unsigned VertexBuffer::GetVertexSize(unsigned elementMask)
+{
+    unsigned vertexSize = 0;
+    
+    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    {
+        if (elementMask & (1 << i))
+            vertexSize += elementSize[i];
+    }
+    
+    return vertexSize;
+}
+
+unsigned VertexBuffer::GetElementOffset(unsigned elementMask, VertexElement element)
+{
+    unsigned offset = 0;
+    
+    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    {
+        if (i == element)
+            break;
+        
+        if (elementMask & (1 << i))
+            offset += elementSize[i];
+    }
+    
+    return offset;
+}
+
+bool VertexBuffer::Create()
+{
+    Release();
+    
+    if (!vertexCount_ || !elementMask_)
+        return true;
+    
+    if (graphics_)
+    {
+        D3D11_BUFFER_DESC bufferDesc;
+        memset(&bufferDesc, 0, sizeof bufferDesc);
+        bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+        bufferDesc.CPUAccessFlags = dynamic_ ? D3D11_CPU_ACCESS_WRITE : 0;
+        bufferDesc.Usage = dynamic_ ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+        bufferDesc.ByteWidth = (unsigned)(vertexCount_ * vertexSize_);
+
+        graphics_->GetImpl()->GetDevice()->CreateBuffer(&bufferDesc, 0, (ID3D11Buffer**)&object_);
+
+        if (!object_)
+        {
+            LOGERROR("Failed to create vertex buffer");
+            return false;
+        }
+    }
+    
+    return true;
+}
+
+bool VertexBuffer::UpdateToGPU()
+{
+    if (object_ && shadowData_)
+        return SetData(shadowData_.Get());
+    else
+        return false;
+}
+
+void* VertexBuffer::MapBuffer(unsigned start, unsigned count, bool discard)
+{
+    void* hwData = 0;
+    
+    if (object_)
+    {
+        D3D11_MAPPED_SUBRESOURCE mappedData;
+        mappedData.pData = 0;
+
+        graphics_->GetImpl()->GetDeviceContext()->Map((ID3D11Buffer*)object_, 0, discard ? D3D11_MAP_WRITE_DISCARD :
+            D3D11_MAP_WRITE, 0, &mappedData);
+        hwData = mappedData.pData;
+        if (!hwData)
+            LOGERROR("Failed to map vertex buffer");
+        else
+            lockState_ = LOCK_HARDWARE;
+    }
+    
+    return hwData;
+}
+
+void VertexBuffer::UnmapBuffer()
+{
+    if (object_ && lockState_ == LOCK_HARDWARE)
+    {
+        graphics_->GetImpl()->GetDeviceContext()->Unmap((ID3D11Buffer*)object_, 0);
+        lockState_ = LOCK_NONE;
+    }
+}
+
+}

+ 130 - 0
Source/Atomic/Graphics/Direct3D11/D3D11VertexBuffer.h

@@ -0,0 +1,130 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+
+namespace Urho3D
+{
+
+/// Hardware vertex buffer.
+class URHO3D_API VertexBuffer : public Object, public GPUObject
+{
+    OBJECT(VertexBuffer);
+    
+public:
+    /// Construct.
+    VertexBuffer(Context* context);
+    /// Destruct.
+    virtual ~VertexBuffer();
+    
+    /// Release buffer.
+    virtual void Release();
+    
+    /// Enable shadowing in CPU memory. Shadowing is forced on if the graphics subsystem does not exist.
+    void SetShadowed(bool enable);
+    /// Set size and vertex elements and dynamic mode. Previous data will be lost.
+    bool SetSize(unsigned vertexCount, unsigned elementMask, bool dynamic = false);
+    /// Set all data in the buffer.
+    bool SetData(const void* data);
+    /// Set a data range in the buffer. Optionally discard data outside the range.
+    bool SetDataRange(const void* data, unsigned start, unsigned count, bool discard = false);
+    /// Lock the buffer for write-only editing. Return data pointer if successful. Optionally discard data outside the range.
+    void* Lock(unsigned start, unsigned count, bool discard = false);
+    /// Unlock the buffer and apply changes to the GPU buffer.
+    void Unlock();
+    
+    /// Return whether CPU memory shadowing is enabled.
+    bool IsShadowed() const { return shadowed_; }
+    /// Return whether is dynamic.
+    bool IsDynamic() const { return dynamic_; }
+    /// Return whether is currently locked.
+    bool IsLocked() const { return lockState_ != LOCK_NONE; }
+    /// Return number of vertices.
+    unsigned GetVertexCount() const {return vertexCount_; }
+    /// Return vertex size.
+    unsigned GetVertexSize() const { return vertexSize_; }
+    /// Return bitmask of vertex elements.
+    unsigned GetElementMask() const { return elementMask_; }
+    /// Return offset of a specified element within a vertex.
+    unsigned GetElementOffset(VertexElement element) const { return elementOffset_[element]; }
+    /// Return buffer hash for building vertex declarations.
+    unsigned long long GetBufferHash(unsigned streamIndex, unsigned useMask);
+    /// Return CPU memory shadow data.
+    unsigned char* GetShadowData() const { return shadowData_.Get(); }
+    /// Return shared array pointer to the CPU memory shadow data.
+    SharedArrayPtr<unsigned char> GetShadowDataShared() const { return shadowData_; }
+
+    /// Return vertex size corresponding to a vertex element mask.
+    static unsigned GetVertexSize(unsigned elementMask);
+    /// Return element offset from an element mask.
+    static unsigned GetElementOffset(unsigned elementMask, VertexElement element);
+    
+    /// Vertex element sizes.
+    static const unsigned elementSize[];
+    /// Vertex element semantic names.
+    static const char* elementSemantics[];
+    /// Vertex element semantic indices.
+    static const unsigned elementSemanticIndices[];
+    /// Vertex element formats.
+    static const unsigned elementFormats[];
+
+private:
+    /// Update offsets of vertex elements.
+    void UpdateOffsets();
+    /// Create buffer.
+    bool Create();
+    /// Update the shadow data to the GPU buffer.
+    bool UpdateToGPU();
+    /// Map the GPU buffer into CPU memory.
+    void* MapBuffer(unsigned start, unsigned count, bool discard);
+    /// Unmap the GPU buffer.
+    void UnmapBuffer();
+    
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Number of vertices.
+    unsigned vertexCount_;
+    /// Vertex size.
+    unsigned vertexSize_;
+    /// Vertex element bitmask.
+    unsigned elementMask_;
+   /// Vertex element offsets.
+    unsigned elementOffset_[MAX_VERTEX_ELEMENTS];
+    /// Buffer locking state.
+    LockState lockState_;
+    /// Lock start vertex.
+    unsigned lockStart_;
+    /// Lock number of vertices.
+    unsigned lockCount_;
+    /// Scratch buffer for fallback locking.
+    void* lockScratchData_;
+    /// Dynamic flag.
+    bool dynamic_;
+    /// Shadowed flag.
+    bool shadowed_;
+};
+
+}

+ 90 - 0
Source/Atomic/Graphics/Direct3D11/D3D11VertexDeclaration.cpp

@@ -0,0 +1,90 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../Graphics/ShaderVariation.h"
+#include "../../Graphics/VertexBuffer.h"
+#include "../../Graphics/VertexDeclaration.h"
+#include "../../IO/Log.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+VertexDeclaration::VertexDeclaration(Graphics* graphics, ShaderVariation* vertexShader, VertexBuffer** vertexBuffers, unsigned* elementMasks) :
+    inputLayout_(0)
+{
+    PODVector<D3D11_INPUT_ELEMENT_DESC> elementDescs;
+
+    unsigned vbElementMask = 0;
+
+    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
+    {
+        if (vertexBuffers[i] && elementMasks[i])
+        {
+            for (unsigned j = 0; j < MAX_VERTEX_ELEMENTS; ++j)
+            {
+                if (elementMasks[i] & (1 << j))
+                {
+                    D3D11_INPUT_ELEMENT_DESC newDesc;
+                    newDesc.SemanticName = VertexBuffer::elementSemantics[j];
+                    newDesc.SemanticIndex = VertexBuffer::elementSemanticIndices[j];
+                    newDesc.Format = (DXGI_FORMAT)VertexBuffer::elementFormats[j];
+                    newDesc.InputSlot = (unsigned)i;
+                    newDesc.AlignedByteOffset = vertexBuffers[i]->GetElementOffset((VertexElement)j);
+                    newDesc.InputSlotClass = (j >= ELEMENT_INSTANCEMATRIX1 && j <= ELEMENT_INSTANCEMATRIX3) ?
+                        D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
+                    newDesc.InstanceDataStepRate = (j >= ELEMENT_INSTANCEMATRIX1 && j <= ELEMENT_INSTANCEMATRIX3) ? 1 : 0;
+                    elementDescs.Push(newDesc);
+                    vbElementMask |= 1 << j;
+                }
+            }
+        }
+    }
+
+    if (elementDescs.Empty())
+        return;
+
+    ID3D11InputLayout* d3dInputLayout = 0;
+    const PODVector<unsigned char>& byteCode = vertexShader->GetByteCode();
+
+    graphics->GetImpl()->GetDevice()->CreateInputLayout(&elementDescs[0], (unsigned)elementDescs.Size(), &byteCode[0],
+        byteCode.Size(), &d3dInputLayout);
+    if (d3dInputLayout)
+        inputLayout_ = d3dInputLayout;
+    else
+        LOGERRORF("Failed to create input layout for shader %s, missing element mask %d",
+            vertexShader->GetFullName().CString(), vertexShader->GetElementMask() & ~vbElementMask);
+}
+
+VertexDeclaration::~VertexDeclaration()
+{
+    if (inputLayout_)
+    {
+        ((ID3D11InputLayout*)inputLayout_)->Release();
+        inputLayout_ = 0;
+    }
+}
+
+}

+ 53 - 0
Source/Atomic/Graphics/Direct3D11/D3D11VertexDeclaration.h

@@ -0,0 +1,53 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/RefCounted.h"
+#include "../../Container/Vector.h"
+
+namespace Urho3D
+{
+
+class Graphics;
+class ShaderVariation;
+class VertexBuffer;
+
+/// Vertex declaration.
+class URHO3D_API VertexDeclaration : public RefCounted
+{
+public:
+    /// Construct with vertex buffers and element masks to base declaration on.
+    VertexDeclaration(Graphics* graphics, ShaderVariation* vertexShader, VertexBuffer** buffers, unsigned* elementMasks);
+    /// Destruct.
+    ~VertexDeclaration();
+    
+    /// Return input layout object corresponding to the declaration.
+    void* GetInputLayout() const { return inputLayout_; }
+    
+private:
+    /// Input layout object.
+    void* inputLayout_;
+};
+
+}

+ 54 - 0
Source/Atomic/Graphics/Direct3D9/D3D9ShaderProgram.h

@@ -0,0 +1,54 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Container/HashMap.h"
+#include "../../Graphics/ShaderVariation.h"
+
+namespace Urho3D
+{
+
+/// Combined information for specific vertex and pixel shaders.
+class ShaderProgram : public RefCounted
+{
+public:
+    /// Construct.
+    ShaderProgram(ShaderVariation* vertexShader, ShaderVariation* pixelShader)
+    {
+        const HashMap<StringHash, ShaderParameter>& vsParams = vertexShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = vsParams.Begin(); i != vsParams.End(); ++i)
+        parameters_[i->first_] = i->second_;
+
+        const HashMap<StringHash, ShaderParameter>& psParams = pixelShader->GetParameters();
+        for (HashMap<StringHash, ShaderParameter>::ConstIterator i = psParams.Begin(); i != psParams.End(); ++i)
+            parameters_[i->first_] = i->second_;
+    
+        // Optimize shader parameter lookup by rehashing to next power of two
+        parameters_.Rehash(NextPowerOfTwo(parameters_.Size()));
+    }
+
+    /// Combined parameters from the vertex and pixel shader.
+    HashMap<StringHash, ShaderParameter> parameters_;
+};
+
+}

+ 139 - 0
Source/Atomic/Graphics/OpenGL/OGLConstantBuffer.cpp

@@ -0,0 +1,139 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Graphics/Graphics.h"
+#include "../../Graphics/GraphicsImpl.h"
+#include "../../IO/Log.h"
+#include "../../Graphics/ConstantBuffer.h"
+
+#include "../../DebugNew.h"
+
+namespace Urho3D
+{
+
+
+ConstantBuffer::ConstantBuffer(Context* context) :
+    Object(context),
+    GPUObject(GetSubsystem<Graphics>())
+{
+}
+
+ConstantBuffer::~ConstantBuffer()
+{
+    Release();
+}
+
+void ConstantBuffer::Release()
+{
+    if (object_)
+    {
+        if (!graphics_)
+            return;
+
+        #ifndef GL_ES_VERSION_2_0
+        graphics_->SetUBO(0);
+        glDeleteBuffers(1, &object_);
+        #endif
+        object_ = 0;
+    }
+
+    shadowData_.Reset();
+    size_ = 0;
+}
+
+void ConstantBuffer::OnDeviceReset()
+{
+    if (size_)
+        SetSize(size_); // Recreate
+}
+
+bool ConstantBuffer::SetSize(unsigned size)
+{
+    if (!size)
+    {
+        LOGERROR("Can not create zero-sized constant buffer");
+        return false;
+    }
+
+    // Round up to next 16 bytes
+    size += 15;
+    size &= 0xfffffff0;
+
+    size_ = size;
+    dirty_ = false;
+    shadowData_ = new unsigned char[size_];
+    memset(shadowData_.Get(), 0, size_);
+
+    if (graphics_)
+    {
+        #ifndef GL_ES_VERSION_2_0
+        if (!object_)
+            glGenBuffers(1, &object_);
+        graphics_->SetUBO(object_);
+        glBufferData(GL_UNIFORM_BUFFER, size_, shadowData_.Get(), GL_DYNAMIC_DRAW);
+        #endif
+    }
+
+    return true;
+}
+
+void ConstantBuffer::SetParameter(unsigned offset, unsigned size, const void* data)
+{
+    if (offset + size > size_)
+        return; // Would overflow the buffer
+
+    memcpy(&shadowData_[offset], data, size);
+    dirty_ = true;
+}
+
+void ConstantBuffer::SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data)
+{
+    if (offset + rows * 4 * sizeof(float) > size_)
+        return; // Would overflow the buffer
+
+    float* dest = (float*)&shadowData_[offset];
+    const float* src = (const float*)data;
+
+    while (rows--)
+    {
+        *dest++ = *src++;
+        *dest++ = *src++;
+        *dest++ = *src++;
+        ++dest; // Skip over the w coordinate
+    }
+
+    dirty_ = true;
+}
+
+void ConstantBuffer::Apply()
+{
+    if (dirty_ && object_)
+    {
+        #ifndef GL_ES_VERSION_2_0
+        graphics_->SetUBO(object_);
+        glBufferData(GL_UNIFORM_BUFFER, size_, shadowData_.Get(), GL_DYNAMIC_DRAW);
+        #endif
+        dirty_ = false;
+    }
+}
+
+}

+ 75 - 0
Source/Atomic/Graphics/OpenGL/OGLConstantBuffer.h

@@ -0,0 +1,75 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../../Graphics/GPUObject.h"
+#include "../../Graphics/GraphicsDefs.h"
+#include "../../Container/ArrayPtr.h"
+#include "../../Core/Object.h"
+
+namespace Urho3D
+{
+
+/// Hardware constant buffer.
+class URHO3D_API ConstantBuffer : public Object, public GPUObject
+{
+    OBJECT(ConstantBuffer);
+    
+public:
+    /// Construct.
+    ConstantBuffer(Context* context);
+    /// Destruct.
+    virtual ~ConstantBuffer();
+    
+    /// Recreate the GPU resource and restore data if applicable.
+    virtual void OnDeviceReset();
+    /// Release the buffer.
+    virtual void Release();
+    
+    /// Set size and create GPU-side buffer. Return true on success.
+    bool SetSize(unsigned size);
+    /// Set a generic parameter and mark buffer dirty.
+    void SetParameter(unsigned offset, unsigned size, const void* data);
+    /// Set a Vector3 array parameter and mark buffer dirty.
+    void SetVector3ArrayParameter(unsigned offset, unsigned rows, const void* data);
+    /// Apply to GPU.
+    void Apply();
+
+    /// Return size.
+    unsigned GetSize() const { return size_; }
+    /// Return whether has unapplied data.
+    bool IsDirty() const { return dirty_; }
+
+private:
+    /// Create buffer.
+    bool Create();
+
+    /// Shadow data.
+    SharedArrayPtr<unsigned char> shadowData_;
+    /// Buffer byte size.
+    unsigned size_;
+    /// Dirty flag.
+    bool dirty_;
+};
+
+}

+ 509 - 0
Source/Atomic/Navigation/CrowdAgent.cpp

@@ -0,0 +1,509 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../Scene/Component.h"
+#include "../Core/Context.h"
+#include "../Navigation/CrowdAgent.h"
+#include "../Graphics/DebugRenderer.h"
+#include "../Navigation/DetourCrowdManager.h"
+#include "../IO/Log.h"
+#include "../IO/MemoryBuffer.h"
+#include "../Navigation/NavigationEvents.h"
+#include "../Scene/Node.h"
+#include "../Core/Profiler.h"
+#include "../Scene/Scene.h"
+#include "../Scene/Serializable.h"
+#include "../Core/Variant.h"
+
+#include <Detour/DetourCommon.h>
+#include <DetourCrowd/DetourCrowd.h>
+
+#include "../DebugNew.h"
+
+namespace Urho3D
+{
+
+extern const char* NAVIGATION_CATEGORY;
+
+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 NavigationQuality DEFAULT_AGENT_AVOIDANCE_QUALITY = NAVIGATIONQUALITY_HIGH;
+static const NavigationPushiness DEFAULT_AGENT_NAVIGATION_PUSHINESS = PUSHINESS_MEDIUM;
+
+const char* crowdAgentAvoidanceQualityNames[] = {
+    "low",
+    "medium",
+    "high",
+    0
+};
+
+const char* crowdAgentPushinessNames[] = {
+    "low",
+    "medium",
+    "high",
+    0
+};
+
+
+CrowdAgent::CrowdAgent(Context* context) :
+    Component(context),
+    inCrowd_(false),
+    agentCrowdId_(-1),
+    targetRef_(-1),
+    updateNodePosition_(true),
+    maxAccel_(DEFAULT_AGENT_MAX_ACCEL),
+    maxSpeed_(DEFAULT_AGENT_MAX_SPEED),
+    radius_(0.0f),
+    height_(0.0f),
+    filterType_(DEFAULT_AGENT_NAVIGATION_FILTER_TYPE),
+    navQuality_(DEFAULT_AGENT_AVOIDANCE_QUALITY),
+    navPushiness_(DEFAULT_AGENT_NAVIGATION_PUSHINESS),
+    previousTargetState_(CROWD_AGENT_TARGET_NONE),
+    previousAgentState_(CROWD_AGENT_READY),
+    ignoreTransformChanges_(false)
+{
+}
+
+CrowdAgent::~CrowdAgent()
+{
+}
+
+void CrowdAgent::RegisterObject(Context* context)
+{
+    context->RegisterFactory<CrowdAgent>(NAVIGATION_CATEGORY);
+
+    ACCESSOR_ATTRIBUTE("Max Accel", GetMaxAccel, SetMaxAccel, float, DEFAULT_AGENT_MAX_ACCEL, 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("Height", GetHeight, SetHeight, float, 0.0f, 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 Quality", GetNavigationQuality, SetNavigationQuality, NavigationQuality, crowdAgentAvoidanceQualityNames, NAVIGATIONQUALITY_LOW, AM_DEFAULT);
+    MIXED_ACCESSOR_ATTRIBUTE("Agent Data", GetAgentDataAttr, SetAgentDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
+}
+
+void CrowdAgent::OnNodeSet(Node* node)
+{
+    if (node)
+    {
+        Scene* scene = GetScene();
+        if (scene)
+        {
+            if (scene == node)
+                LOGERROR(GetTypeName() + " should not be created to the root scene node");
+            crowdManager_ = scene->GetOrCreateComponent<DetourCrowdManager>();
+            AddAgentToCrowd();
+        }
+
+        node->AddListener(this);
+    }
+}
+
+void CrowdAgent::OnSetEnabled()
+{
+    bool enabled = IsEnabledEffective();
+
+    if (enabled && !inCrowd_)
+        AddAgentToCrowd();
+    else if (!enabled && inCrowd_)
+        RemoveAgentFromCrowd();
+}
+
+void CrowdAgent::DrawDebugGeometry(bool depthTest)
+{
+    Scene* scene = GetScene();
+    if (scene)
+    {
+        DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
+        if (debug)
+            DrawDebugGeometry(debug, depthTest);
+    }
+}
+
+void CrowdAgent::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (node_)
+    {
+        const Vector3 pos = GetPosition();
+        const Vector3 vel = GetActualVelocity();
+        const Vector3 desiredVel = GetDesiredVelocity();
+        const Vector3 agentHeightVec(0, height_ * 0.5f, 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);
+    }
+}
+
+void CrowdAgent::AddAgentToCrowd()
+{
+    if (!crowdManager_ || !crowdManager_->crowd_)
+        return;
+
+    PROFILE(AddAgentToCrowd);
+
+    if (!inCrowd_)
+    {
+        inCrowd_ = true;
+        agentCrowdId_ = crowdManager_->AddAgent(this, node_->GetPosition());
+        if (agentCrowdId_ == -1)
+        {
+            inCrowd_ = false;
+            LOGERROR("AddAgentToCrowd: Could not add agent to crowd");
+            return;
+        }
+        dtCrowdAgentParams& params = crowdManager_->GetCrowd()->getEditableAgent(agentCrowdId_)->params;
+        params.userData = this;
+        crowdManager_->UpdateAgentNavigationQuality(this, navQuality_);
+        crowdManager_->UpdateAgentPushiness(this, navPushiness_);
+        previousAgentState_ = GetAgentState();
+        previousTargetState_ = GetTargetState();
+
+        // Agent created, but initial state is invalid and needs to be addressed
+        if (previousAgentState_ == CROWD_AGENT_INVALID)
+        {
+            VariantMap& map = GetContext()->GetEventDataMap();
+            map[CrowdAgentFailure::P_NODE] = GetNode();
+            map[CrowdAgentFailure::P_CROWD_AGENT] = this;
+            map[CrowdAgentFailure::P_CROWD_TARGET_STATE] = previousTargetState_;
+            map[CrowdAgentFailure::P_CROWD_AGENT_STATE] = previousAgentState_;
+            map[CrowdAgentFailure::P_POSITION] = GetPosition();
+            map[CrowdAgentFailure::P_VELOCITY] = GetActualVelocity();
+            SendEvent(E_CROWD_AGENT_FAILURE, map);
+
+            // Reevaluate states as handling of event may have resulted in changes
+            previousAgentState_ = GetAgentState();
+            previousTargetState_ = GetTargetState();
+        }
+    }
+}
+
+void CrowdAgent::RemoveAgentFromCrowd()
+{
+    if (crowdManager_ && agentCrowdId_ != -1 && inCrowd_)
+    {
+        crowdManager_->RemoveAgent(this);
+        inCrowd_ = false;
+        agentCrowdId_ = -1;
+    }
+}
+
+void CrowdAgent::SetNavigationFilterType(unsigned filterType)
+{
+    filterType_ = filterType;
+    if (crowdManager_ && inCrowd_)
+    {
+        // If in the crowd it's necessary to force the update of the query filter
+        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        params.queryFilterType = (unsigned char)filterType;
+        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        MarkNetworkUpdate();
+    }
+}
+
+bool CrowdAgent::SetMoveTarget(const Vector3& position)
+{
+    if (crowdManager_ && !inCrowd_)
+        AddAgentToCrowd();
+    if (crowdManager_ && inCrowd_)
+    {
+        targetPosition_ = position;
+        if (crowdManager_->SetAgentTarget(this, position, targetRef_))
+        {
+            MarkNetworkUpdate();
+            return true;
+        }
+    }
+    return false;
+}
+
+bool CrowdAgent::SetMoveVelocity(const Vector3& velocity)
+{
+    if (crowdManager_ && inCrowd_)
+    {
+        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
+        if (agent && agent->active)
+        {
+            crowdManager_->GetCrowd()->requestMoveVelocity(agentCrowdId_, velocity.Data());
+            MarkNetworkUpdate();
+        }
+    }
+    return false;
+}
+
+void CrowdAgent::SetMaxSpeed(float speed)
+{
+    maxSpeed_ = speed;
+    if(crowdManager_ && inCrowd_)
+    {
+        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        params.maxSpeed = speed;
+        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdAgent::SetMaxAccel(float accel)
+{
+    maxAccel_ = accel;
+    if(crowdManager_ && inCrowd_)
+    {
+        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        params.maxAcceleration = accel;
+        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdAgent::SetRadius(float radius)
+{
+    radius_ = radius;
+    if (crowdManager_ && inCrowd_)
+    {
+        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        params.radius = radius;
+        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdAgent::SetHeight(float height)
+{
+    height_ = height;
+    if (crowdManager_ && inCrowd_)
+    {
+        dtCrowdAgentParams params = crowdManager_->GetCrowdAgent(agentCrowdId_)->params;
+        params.height = height;
+        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdAgent::SetNavigationQuality(NavigationQuality val)
+{
+    navQuality_ = val;
+    if(crowdManager_ && inCrowd_)
+    {
+        crowdManager_->UpdateAgentNavigationQuality(this, navQuality_);
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdAgent::SetNavigationPushiness(NavigationPushiness val)
+{
+    navPushiness_ = val;
+    if(crowdManager_ && inCrowd_)
+    {
+        crowdManager_->UpdateAgentPushiness(this, navPushiness_);
+        MarkNetworkUpdate();
+    }
+}
+
+Vector3 CrowdAgent::GetPosition() const
+{
+    if (crowdManager_ && inCrowd_)
+    {
+        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
+        if (agent && agent->active)
+            return Vector3(agent->npos);
+    }
+    return node_->GetPosition();
+}
+
+Vector3 CrowdAgent::GetDesiredVelocity() const
+{
+    if (crowdManager_ && inCrowd_)
+    {
+        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
+        if (agent && agent->active)
+            return Vector3(agent->dvel);
+    }
+    return Vector3::ZERO;
+}
+
+Vector3 CrowdAgent::GetActualVelocity() const
+{
+    if (crowdManager_ && inCrowd_)
+    {
+        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
+        if (agent && agent->active)
+            return Vector3(agent->vel);
+    }
+    return Vector3::ZERO;
+}
+
+Urho3D::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;
+}
+
+Urho3D::CrowdTargetState CrowdAgent::GetTargetState() const
+{
+    if (crowdManager_ && inCrowd_)
+    {
+        const dtCrowdAgent* agent = crowdManager_->GetCrowdAgent(agentCrowdId_);
+        if (!agent || !agent->active)
+            return 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;
+}
+
+void CrowdAgent::SetUpdateNodePosition(bool unodepos)
+{
+    updateNodePosition_ = unodepos;
+    MarkNetworkUpdate();
+}
+
+void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newDirection)
+{
+    if (node_)
+    {
+        // 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_)
+        {
+            ignoreTransformChanges_ = true;
+            node_->SetPosition(newPos);
+            ignoreTransformChanges_ = false;
+        }
+
+        // Send a notification event if we've reached the destination
+        CrowdTargetState newTargetState = GetTargetState();
+        CrowdAgentState newAgentState = GetAgentState();
+        if (newAgentState != previousAgentState_ || newTargetState != previousTargetState_)
+        {
+            VariantMap& map = GetContext()->GetEventDataMap();
+            map[CrowdAgentStateChanged::P_NODE] = GetNode();
+            map[CrowdAgentStateChanged::P_CROWD_AGENT] = this;
+            map[CrowdAgentStateChanged::P_CROWD_TARGET_STATE] = newTargetState;
+            map[CrowdAgentStateChanged::P_CROWD_AGENT_STATE] = newAgentState;
+            map[CrowdAgentStateChanged::P_POSITION] = newPos;
+            map[CrowdAgentStateChanged::P_VELOCITY] = GetActualVelocity();
+            SendEvent(E_CROWD_AGENT_STATE_CHANGED, map);
+
+            // Send a failure event if either state is a failed status
+            if (newAgentState == CROWD_AGENT_INVALID || newTargetState == CROWD_AGENT_TARGET_FAILED)
+            {
+                VariantMap& map = GetContext()->GetEventDataMap();
+                map[CrowdAgentFailure::P_NODE] = GetNode();
+                map[CrowdAgentFailure::P_CROWD_AGENT] = this;
+                map[CrowdAgentFailure::P_CROWD_TARGET_STATE] = newTargetState;
+                map[CrowdAgentFailure::P_CROWD_AGENT_STATE] = newAgentState;
+                map[CrowdAgentFailure::P_POSITION] = newPos;
+                map[CrowdAgentFailure::P_VELOCITY] = GetActualVelocity();
+                SendEvent(E_CROWD_AGENT_FAILURE, map);
+            }
+
+            // State may have been altered during the handling of the event
+            previousAgentState_ = GetAgentState();
+            previousTargetState_ = GetTargetState();
+        }
+    }
+}
+
+PODVector<unsigned char> CrowdAgent::GetAgentDataAttr() const
+{
+    if (!inCrowd_ || !crowdManager_ || !IsEnabled())
+        return Variant::emptyBuffer;
+    dtCrowd* crowd = crowdManager_->GetCrowd();
+    const dtCrowdAgent* agent = crowd->getAgent(agentCrowdId_);
+    
+    // Reading it back in isn't this simple, see SetAgentDataAttr
+    VectorBuffer ret;
+    ret.Write(agent, sizeof(dtCrowdAgent));
+
+    return ret.GetBuffer();
+}
+
+void CrowdAgent::SetAgentDataAttr(const PODVector<unsigned char>& value)
+{
+    if (value.Empty() || !inCrowd_ || !crowdManager_ || !IsEnabled())
+        return;
+
+    MemoryBuffer buffer(value);
+    dtCrowd* crowd = crowdManager_->GetCrowd();
+    dtCrowdAgent* agent = crowd->getEditableAgent(agentCrowdId_);
+
+    // Path corridor is tricky
+    char corridorData[sizeof(dtPathCorridor)];
+    // Duplicate the existing path corridor into a block
+    memcpy(corridorData, &agent->corridor, sizeof(dtPathCorridor));
+
+    // Read the entire block of the crowd agent
+    buffer.Read(agent, sizeof(dtCrowdAgent));
+    // Restore the values of the original path corridor
+    memcpy(&agent->corridor, corridorData, sizeof(dtPathCorridor));
+    // Tell the path corridor to rebuild the path, it will reevaluate the path, existing velocities maintained
+    agent->corridor.reset(agent->targetRef, agent->targetPos);
+
+    agent->params.userData = this;
+}
+
+void CrowdAgent::OnMarkedDirty(Node* node)
+{
+    if (inCrowd_ && crowdManager_ && !ignoreTransformChanges_ && IsEnabledEffective())
+    {
+        dtCrowdAgent* agt = crowdManager_->GetCrowd()->getEditableAgent(agentCrowdId_);
+        if (agt)
+        {
+            memcpy(agt->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 (agt->state == CROWD_AGENT_INVALID)
+                agt->state = CROWD_AGENT_READY;
+        }
+    }
+}
+
+
+}

+ 174 - 0
Source/Atomic/Navigation/CrowdAgent.h

@@ -0,0 +1,174 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Scene/Component.h"
+#include "../Navigation/DetourCrowdManager.h"
+
+namespace Urho3D
+{
+
+enum CrowdTargetState
+{
+    CROWD_AGENT_TARGET_NONE = 0,
+    CROWD_AGENT_TARGET_FAILED,
+    CROWD_AGENT_TARGET_VALID,
+    CROWD_AGENT_TARGET_REQUESTING,
+    CROWD_AGENT_TARGET_WAITINGFORPATH,
+    CROWD_AGENT_TARGET_WAITINGFORQUEUE,
+    CROWD_AGENT_TARGET_VELOCITY,
+    CROWD_AGENT_TARGET_ARRIVED
+};
+
+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.
+};
+
+/// DetourCrowd Agent, requires a DetourCrowdManager in the scene. Agent's radius and height is set through the navigation mesh.
+class URHO3D_API CrowdAgent : public Component
+{
+    OBJECT(CrowdAgent);
+    friend class DetourCrowdManager;
+
+public:
+    /// Construct.
+    CrowdAgent(Context* context);
+    /// Destruct.
+    virtual ~CrowdAgent();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+    /// Handle enabled/disabled state change.
+    virtual void OnSetEnabled();
+    /// Draw debug geometry.
+    void DrawDebugGeometry(bool);
+    /// Draw debug feelers.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+
+    /// Set the navigation filter type the agent will use.
+    void SetNavigationFilterType(unsigned filterTypeID);
+    /// Submit a new move request for this agent.
+    bool SetMoveTarget(const Vector3& position);
+    /// Submit a new move velocity request for this agent.
+    bool 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.
+    void SetUpdateNodePosition(bool unodepos);
+    /// Set the agent's max acceleration.
+    void SetMaxAccel(float val);
+    /// Set the agent's max velocity.
+    void SetMaxSpeed(float val);
+    /// Set the agent's radius.
+    void SetRadius(float val);
+    /// Set the agent's height.
+    void SetHeight(float val);
+    /// Set the agent's navigation quality.
+    void SetNavigationQuality(NavigationQuality val);
+    /// Set the agent's navigation pushiness.
+    void SetNavigationPushiness(NavigationPushiness val);
+
+    /// Get the navigation filter type this agent is using.
+    unsigned GetNavigationFilterType() const { return filterType_; }
+    /// Return the agent's position.
+    Vector3 GetPosition() const;
+    /// Return the agent's desired velocity.
+    Vector3 GetDesiredVelocity() const;
+    /// Return the agent's actual velocity.
+    Vector3 GetActualVelocity() const;
+    /// Return the agent's target position.
+    const Vector3& GetTargetPosition() const { return targetPosition_; }
+    /// Return the agent's  state.
+    CrowdAgentState GetAgentState() const;
+    /// Return the agent's target state.
+    CrowdTargetState GetTargetState() const;
+    /// Return true when the node's position should be updated by the CrowdManager.
+    bool GetUpdateNodePosition() const { return updateNodePosition_; }
+    /// Return the agent id.
+    int GetAgentCrowdId() const { return agentCrowdId_; }
+    /// Get the agent's max velocity.
+    float GetMaxSpeed() const { return maxSpeed_; }
+    /// Get the agent's max acceleration.
+    float GetMaxAccel()	const { return maxAccel_; }
+    /// Get the agent's radius.
+    float GetRadius() const { return radius_; }
+    /// Get the agent's height.
+    float GetHeight() const { return height_; }
+    /// Get the agent's navigation quality.
+    NavigationQuality GetNavigationQuality() const {return navQuality_; }
+    /// Get the agent's navigation pushiness.
+    NavigationPushiness GetNavigationPushiness() const {return navPushiness_; }
+
+    /// Get serialized data of internal state.
+    PODVector<unsigned char> GetAgentDataAttr() const;
+    /// Set serialized data of internal state.
+    void SetAgentDataAttr(const PODVector<unsigned char>& value);
+
+protected:
+    /// Update the nodes position if updateNodePosition is set. Is called in DetourCrowdManager::Update().
+    virtual void OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newDirection);
+    /// Handle node being assigned.
+    virtual void OnNodeSet(Node* node);
+    /// \todo Handle node transform being dirtied.
+    virtual void OnMarkedDirty(Node* node);
+private:
+    /// Create or re-add.
+    void AddAgentToCrowd();
+    /// Remove.
+    void RemoveAgentFromCrowd();
+    /// Detour crowd manager.
+    WeakPtr<DetourCrowdManager> crowdManager_;
+    /// Flag indicating agent is in DetourCrowd.
+    bool inCrowd_;
+    /// DetourCrowd reference to this agent.
+    int agentCrowdId_;
+    /// Reference to poly closest to requested target position.
+    unsigned int targetRef_;         
+    /// Actual target position, closest to that requested.
+    Vector3 targetPosition_;   
+    /// Flag indicating the node's position should be updated by Detour crowd manager.
+    bool updateNodePosition_;
+    /// Agent's max acceleration.
+    float maxAccel_;
+    /// Agent's max Velocity.
+    float maxSpeed_;
+    /// Agent's radius, if 0 the navigation mesh's setting will be used.
+    float radius_;
+    /// Agent's height, if 0 the navigation mesh's setting will be used.
+    float height_;
+    /// Agent's assigned navigation filter type, the actual filter is owned by the DetourCrowdManager the agent belongs to.
+    unsigned filterType_;
+    /// Agent's NavigationAvoidanceQuality.
+    NavigationQuality navQuality_;
+    /// Agent's Navigation Pushiness.
+    NavigationPushiness navPushiness_;
+    /// Agent's previous target state used to check for state changes.
+    CrowdTargetState previousTargetState_;
+    /// Agent's previous agent state used to check for state changes.
+    CrowdAgentState previousAgentState_;
+    /// Internal flag to ignore transform changes because it came from us, used in OnCrowdAgentReposition().
+    bool ignoreTransformChanges_;
+};
+
+}

+ 486 - 0
Source/Atomic/Navigation/DetourCrowdManager.cpp

@@ -0,0 +1,486 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../Scene/Component.h"
+#include "../Core/Context.h"
+#include "../Navigation/CrowdAgent.h"
+#include "../Graphics/DebugRenderer.h"
+#include "../Navigation/DetourCrowdManager.h"
+#include "../Navigation/DynamicNavigationMesh.h"
+#include "../IO/Log.h"
+#include "../Navigation/NavigationEvents.h"
+#include "../Navigation/NavigationMesh.h"
+#include "../Scene/Node.h"
+#include "../Core/Profiler.h"
+#include "../Scene/Scene.h"
+#include "../Scene/SceneEvents.h"
+#include "../Container/Vector.h"
+
+#ifdef URHO3D_PHYSICS
+#include "../Physics/PhysicsEvents.h"
+#endif
+
+#include <DetourCrowd/DetourCrowd.h>
+#include <Recast/Recast.h>
+
+#include "../DebugNew.h"
+
+namespace Urho3D
+{
+    
+extern const char* NAVIGATION_CATEGORY;
+
+static const unsigned DEFAULT_MAX_AGENTS = 512;
+
+DetourCrowdManager::DetourCrowdManager(Context* context) :
+    Component(context),
+    maxAgents_(DEFAULT_MAX_AGENTS),
+    crowd_(0),
+    navigationMesh_(0),
+    agentDebug_(0)
+{
+    agentBuffer_.Resize(maxAgents_);
+}
+
+DetourCrowdManager::~DetourCrowdManager()
+{
+    dtFreeCrowd(crowd_);
+    crowd_ = 0;
+    delete agentDebug_;
+    agentDebug_ = 0;
+}
+
+void DetourCrowdManager::RegisterObject(Context* context)
+{
+    context->RegisterFactory<DetourCrowdManager>(NAVIGATION_CATEGORY);
+    
+    ACCESSOR_ATTRIBUTE("Max Agents", GetMaxAgents, SetMaxAgents, unsigned, DEFAULT_MAX_AGENTS, AM_DEFAULT);
+}
+
+void DetourCrowdManager::SetNavigationMesh(NavigationMesh* navMesh)
+{
+    navigationMesh_ = WeakPtr<NavigationMesh>(navMesh);
+    if (navigationMesh_ && !navigationMesh_->navMeshQuery_)
+        navigationMesh_->InitializeQuery();
+    CreateCrowd();
+    MarkNetworkUpdate();
+}
+
+void DetourCrowdManager::SetAreaCost(unsigned filterID, unsigned areaID, float weight)
+{
+    dtQueryFilter* filter = crowd_->getEditableFilter(filterID);
+    if (filter)
+        filter->setAreaCost((int)areaID, weight);
+}
+
+void DetourCrowdManager::SetMaxAgents(unsigned agentCt)
+{
+    maxAgents_ = agentCt;
+    if (crowd_ && crowd_->getAgentCount() > 0)
+        LOGERROR("DetourCrowdManager contains active agents, their state will be lost");
+    agentBuffer_.Resize(maxAgents_);
+    CreateCrowd();
+    if (crowd_)
+    {
+        PODVector<CrowdAgent*> agents = agents_;
+        // Reset the existing values in the agent
+        for (unsigned i = 0; i < agents.Size(); ++i)
+        {
+            agents[i]->inCrowd_ = false;
+            agents[i]->agentCrowdId_ = -1;
+        }
+        // Add the agents back in
+        for (unsigned i = 0; i < agents.Size() && i < maxAgents_; ++i)
+            agents[i]->AddAgentToCrowd();
+        if (agents.Size() > maxAgents_)
+            LOGERROR("DetourCrowdManager: resize left " + String(agents.Size() - maxAgents_) + " agents orphaned");
+    }
+    MarkNetworkUpdate();
+}
+
+NavigationMesh* DetourCrowdManager::GetNavigationMesh()
+{
+    return navigationMesh_.Get();
+}
+
+float DetourCrowdManager::GetAreaCost(unsigned filterID, unsigned areaID) const
+{
+    if (crowd_ && navigationMesh_)
+    {
+        const dtQueryFilter* filter = crowd_->getFilter((int)filterID);
+        if (filter)
+            return filter->getAreaCost((int)areaID);
+    }
+    return 0.0f;
+}
+
+unsigned DetourCrowdManager::GetAgentCount() const
+{
+    return crowd_ ? crowd_->getAgentCount() : 0;
+}
+
+void DetourCrowdManager::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (debug && navigationMesh_.NotNull() && crowd_)
+    {
+        // Current position-to-target line
+        for (int i = 0; i < crowd_->getAgentCount(); i++)
+        {
+            const dtCrowdAgent* ag = crowd_->getAgent(i);
+            if (!ag->active)
+                continue;
+
+            Color color(0.6f, 0.2f, 0.2f, 1.0f);
+
+            // Render line to target:
+            Vector3 pos1(ag->npos[0], ag->npos[1], ag->npos[2]);
+            Vector3 pos2;
+            for (int i = 0; i < ag->ncorners; ++i)
+            {
+                pos2.x_ = ag->cornerVerts[i * 3];
+                pos2.y_ = ag->cornerVerts[i * 3 + 1];
+                pos2.z_ = ag->cornerVerts[i * 3 + 2];
+                debug->AddLine(pos1, pos2, color, depthTest);
+                pos1 = pos2;
+            }
+            pos2.x_ = ag->targetPos[0];
+            pos2.y_ = ag->targetPos[1];
+            pos2.z_ = ag->targetPos[2];
+            debug->AddLine(pos1, pos2, color, depthTest);
+
+            // Target circle
+            debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
+        }
+    }
+}
+
+bool DetourCrowdManager::CreateCrowd()
+{
+    if (!navigationMesh_ || !navigationMesh_->navMesh_)
+        return false;
+
+    if (crowd_)
+        dtFreeCrowd(crowd_);
+    crowd_ = dtAllocCrowd();
+    if (!agentDebug_)
+        agentDebug_ = new dtCrowdAgentDebugInfo();
+
+    // Initialize the crowd
+    if (!crowd_->init(maxAgents_, navigationMesh_->GetAgentRadius(), navigationMesh_->navMesh_))
+    {
+        LOGERROR("Could not initialize DetourCrowd");
+        return false;
+    }
+
+    // Setup local avoidance params to different qualities.
+    dtObstacleAvoidanceParams params;
+    memcpy(&params, crowd_->getObstacleAvoidanceParams(0), sizeof(dtObstacleAvoidanceParams));
+
+    // Low (11)
+    params.velBias = 0.5f;
+    params.adaptiveDivs = 5;
+    params.adaptiveRings = 2;
+    params.adaptiveDepth = 1;
+    crowd_->setObstacleAvoidanceParams(0, &params);
+
+    // Medium (22)
+    params.velBias = 0.5f;
+    params.adaptiveDivs = 5;
+    params.adaptiveRings = 2;
+    params.adaptiveDepth = 2;
+    crowd_->setObstacleAvoidanceParams(1, &params);
+
+    // Good (45)
+    params.velBias = 0.5f;
+    params.adaptiveDivs = 7;
+    params.adaptiveRings = 2;
+    params.adaptiveDepth = 3;
+    crowd_->setObstacleAvoidanceParams(2, &params);
+
+    // High (66)
+    params.velBias = 0.5f;
+    params.adaptiveDivs = 7;
+    params.adaptiveRings = 3;
+    params.adaptiveDepth = 3;
+    crowd_->setObstacleAvoidanceParams(3, &params);
+
+    return true;
+}
+
+int DetourCrowdManager::AddAgent(CrowdAgent* agent, const Vector3& pos)
+{
+    if (!crowd_ || navigationMesh_.Expired())
+        return -1;
+    dtCrowdAgentParams params;
+    if (agent->radius_ <= 0.0f)
+        agent->radius_ = navigationMesh_->GetAgentRadius();
+    params.radius = agent->radius_;
+    if (agent->height_ <= 0.0f)
+        agent->height_ = navigationMesh_->GetAgentHeight();
+    params.height = agent->height_;
+    params.queryFilterType = (unsigned char)agent->filterType_;
+    params.maxAcceleration = agent->maxAccel_;
+    params.maxSpeed = agent->maxSpeed_;
+    params.collisionQueryRange = params.radius * 8.0f;
+    params.pathOptimizationRange = params.radius * 30.0f;
+    params.updateFlags = DT_CROWD_ANTICIPATE_TURNS
+        | DT_CROWD_OPTIMIZE_VIS
+        | DT_CROWD_OPTIMIZE_TOPO
+        | DT_CROWD_OBSTACLE_AVOIDANCE;
+    params.obstacleAvoidanceType = 3;
+    params.separationWeight = 2.0f;
+    params.queryFilterType = 0;
+    dtPolyRef polyRef;
+    float nearestPos[3];
+    rcVcopy(nearestPos, &pos.x_);
+    dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
+        pos.Data(),
+        crowd_->getQueryExtents(),
+        crowd_->getFilter(agent->filterType_),
+        &polyRef,
+        nearestPos);
+    
+    const int agentID = crowd_->addAgent(nearestPos, &params);
+    if (agentID != -1)
+        agents_.Push(agent);
+    return agentID;
+}
+
+void DetourCrowdManager::RemoveAgent(CrowdAgent* agent)
+{
+    if (!crowd_)
+        return;
+    // Clear user data
+    dtCrowdAgent* agt = crowd_->getEditableAgent(agent->GetAgentCrowdId());
+    if (agt)
+        agt->params.userData = 0;
+    crowd_->removeAgent(agent->GetAgentCrowdId());
+    agents_.Remove(agent);
+}
+
+void DetourCrowdManager::UpdateAgentNavigationQuality(CrowdAgent* agent, NavigationQuality nq)
+{
+    if (!crowd_)
+        return;
+
+    dtCrowdAgentParams params = crowd_->getAgent(agent->GetAgentCrowdId())->params;
+    switch (nq)
+    {
+    case NAVIGATIONQUALITY_LOW:
+        {
+            params.updateFlags &= ~0
+                & ~DT_CROWD_ANTICIPATE_TURNS
+                & ~DT_CROWD_OPTIMIZE_VIS
+                & ~DT_CROWD_OPTIMIZE_TOPO
+                & ~DT_CROWD_OBSTACLE_AVOIDANCE;
+        }
+        break;
+
+    case NAVIGATIONQUALITY_MEDIUM:
+        {
+            params.updateFlags |= 0;
+            params.updateFlags &= ~0
+                & ~DT_CROWD_OBSTACLE_AVOIDANCE
+                & ~DT_CROWD_ANTICIPATE_TURNS
+                & ~DT_CROWD_OPTIMIZE_VIS
+                & ~DT_CROWD_OPTIMIZE_TOPO;
+        }
+        break;
+
+    case NAVIGATIONQUALITY_HIGH:
+        {
+            params.obstacleAvoidanceType = 3;
+            params.updateFlags |= 0
+                | DT_CROWD_ANTICIPATE_TURNS
+                | DT_CROWD_OPTIMIZE_VIS
+                | DT_CROWD_OPTIMIZE_TOPO
+                | DT_CROWD_OBSTACLE_AVOIDANCE;
+        }
+        break;
+    }
+
+    crowd_->updateAgentParameters(agent->GetAgentCrowdId(), &params);
+}
+
+void DetourCrowdManager::UpdateAgentPushiness(CrowdAgent* agent, NavigationPushiness pushiness)
+{
+    if (!crowd_)
+        return;
+
+    dtCrowdAgentParams params = crowd_->getAgent(agent->GetAgentCrowdId())->params;
+    switch (pushiness)
+    {
+    case PUSHINESS_LOW:
+        params.separationWeight = 4.0f;
+        params.collisionQueryRange = params.radius * 16.0f;
+        break;
+
+    case PUSHINESS_MEDIUM:
+        params.separationWeight = 2.0f;
+        params.collisionQueryRange = params.radius * 8.0f;
+        break;
+
+    case PUSHINESS_HIGH:
+        params.separationWeight = 0.5f;
+        params.collisionQueryRange = params.radius * 1.0f;
+        break;
+    }
+    crowd_->updateAgentParameters(agent->GetAgentCrowdId(), &params);
+}
+
+bool DetourCrowdManager::SetAgentTarget(CrowdAgent* agent, Vector3 target)
+{
+    if (!crowd_)
+        return false;
+    dtPolyRef polyRef;
+    float nearestPos[3];
+    dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
+        target.Data(),
+        crowd_->getQueryExtents(),
+        crowd_->getFilter(agent->filterType_),
+        &polyRef,
+        nearestPos);
+
+    return !dtStatusFailed(status) && crowd_->requestMoveTarget(agent->GetAgentCrowdId(), polyRef, nearestPos);
+}
+
+bool DetourCrowdManager::SetAgentTarget(CrowdAgent* agent, Vector3 target, unsigned int& targetRef)
+{
+    if (crowd_ == 0)
+        return false;
+    float nearestPos[3];
+    dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
+        target.Data(),
+        crowd_->getQueryExtents(),
+        crowd_->getFilter(agent->filterType_),
+        &targetRef,
+        nearestPos);
+
+    // Return true if detour has determined it can do something with our move target
+    return !dtStatusFailed(status) && crowd_->requestMoveTarget(agent->GetAgentCrowdId(), targetRef, nearestPos) &&
+        crowd_->getAgent(agent->GetAgentCrowdId())->targetState != DT_CROWDAGENT_TARGET_FAILED;
+}
+
+Vector3 DetourCrowdManager::GetClosestWalkablePosition(Vector3 pos) const
+{
+    if (!crowd_)
+        return Vector3::ZERO;
+    float closest[3];
+    const static float extents[] = { 1.0f, 20.0f, 1.0f };
+    dtPolyRef closestPoly;
+    dtQueryFilter filter;
+    dtStatus status = navigationMesh_->navMeshQuery_->findNearestPoly(
+        pos.Data(),
+        crowd_->getQueryExtents(),
+        &filter,
+        &closestPoly,
+        closest);
+    return Vector3(closest);
+}
+
+void DetourCrowdManager::Update(float delta)
+{
+    if (!crowd_)
+        return;
+
+    PROFILE(UpdateCrowd);
+    
+    crowd_->update(delta, agentDebug_);
+
+    memset(&agentBuffer_[0], 0, maxAgents_ * sizeof(dtCrowdAgent*));
+    const int count = crowd_->getActiveAgents(&agentBuffer_[0], maxAgents_);
+    
+    {
+        PROFILE(ApplyCrowdUpdates);
+        for (int i = 0; i < count; i++)
+        {
+            dtCrowdAgent* agent = agentBuffer_[i];
+            if (agent)
+            {
+                CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(agent->params.userData);    
+                if (crowdAgent)
+                    crowdAgent->OnCrowdAgentReposition(Vector3(agent->npos), Vector3(agent->vel));
+            }
+        }
+    }
+}
+
+const dtCrowdAgent* DetourCrowdManager::GetCrowdAgent(int agent)
+{
+    return crowd_ ? crowd_->getAgent(agent) : 0;
+}
+
+dtCrowd* DetourCrowdManager::GetCrowd()
+{
+    return crowd_;
+}
+
+void DetourCrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
+{
+    using namespace SceneSubsystemUpdate;
+
+    if (IsEnabledEffective())
+        Update(eventData[P_TIMESTEP].GetFloat());
+}
+
+void DetourCrowdManager::HandleNavMeshFullRebuild(StringHash eventType, VariantMap& eventData)
+{
+    using namespace NavigationMeshRebuilt;
+
+    // The mesh being rebuilt may not have existed before
+    NavigationMesh* navMesh = static_cast<NavigationMesh*>(eventData[P_MESH].GetPtr());
+    if (!navigationMesh_ || !crowd_)
+    {
+        SetNavigationMesh(navMesh);
+
+        // Scan for existing agents that are potentially important
+        PODVector<Node*> agents;
+        GetScene()->GetChildrenWithComponent<CrowdAgent>(agents, true);
+        for (unsigned i = 0; i < agents.Size(); ++i)
+        {
+            CrowdAgent* agent = agents[i]->GetComponent<CrowdAgent>();
+            if (agent && agent->IsEnabledEffective())
+                agent->AddAgentToCrowd();
+        }
+    }
+}
+
+void DetourCrowdManager::OnNodeSet(Node* node)
+{
+    // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference
+    // to the scene's NavigationMesh
+    if (node)
+    {
+        SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
+        SubscribeToEvent(node, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
+        
+        NavigationMesh* mesh = GetScene()->GetComponent<NavigationMesh>();
+        if (!mesh)
+            mesh = GetScene()->GetComponent<DynamicNavigationMesh>();
+        if (mesh)
+            SetNavigationMesh(mesh);
+        else
+            LOGERROR("DetourCrowdManager requires an existing navigation mesh");
+    }
+}
+
+}

+ 138 - 0
Source/Atomic/Navigation/DetourCrowdManager.h

@@ -0,0 +1,138 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Scene/Component.h"
+
+class dtCrowd;
+struct dtCrowdAgent;
+struct dtCrowdAgentDebugInfo;
+
+namespace Urho3D
+{
+
+class CrowdAgent;
+class NavigationMesh;
+
+enum NavigationQuality
+{
+    NAVIGATIONQUALITY_LOW = 0,
+    NAVIGATIONQUALITY_MEDIUM = 1,
+    NAVIGATIONQUALITY_HIGH = 2
+};
+
+enum NavigationPushiness
+{
+    PUSHINESS_LOW,
+    PUSHINESS_MEDIUM,
+    PUSHINESS_HIGH
+};
+
+
+/// Detour Crowd Simulation Scene Component. Should be added only to the root scene node. Agent's radius and height is set through the navigation mesh. \todo support multiple agent's radii and heights.
+class URHO3D_API DetourCrowdManager : public Component
+{
+    OBJECT(DetourCrowdManager);
+    friend class CrowdAgent;
+              
+public:
+    /// Construct.
+    DetourCrowdManager(Context* context);
+    /// Destruct.
+    virtual ~DetourCrowdManager();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+    /// Assigns the navigation mesh for the crowd.
+    void SetNavigationMesh(NavigationMesh* navMesh);
+    /// Set the cost of an area-type for the specified navigation filter type.
+    void SetAreaCost(unsigned filterTypeID, unsigned areaID, float weight);
+    /// Set the maximum number of agents.
+    void SetMaxAgents(unsigned agentCt);
+
+    /// Get the Navigation mesh assigned to the crowd.
+    NavigationMesh* GetNavigationMesh();
+    /// Get the cost of an area-type for the specified navigation filter type.
+    float GetAreaCost(unsigned filterTypeID, unsigned areaID) const;
+    /// Get the maximum number of agents.
+    unsigned GetMaxAgents() const { return maxAgents_; }
+    /// Get the current number of active agents.
+    unsigned GetAgentCount() const;
+
+    /// Draw the agents' pathing debug data.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+    /// Get the currently included agents.
+    PODVector<CrowdAgent*> GetActiveAgents() const { return agents_; }
+    /// Create detour crowd component for the specified navigation mesh.
+    bool CreateCrowd();
+
+protected:
+    /// Create and adds an detour crowd agent, Agent's radius and height is set through the navigation mesh. Return -1 on error, agent ID on success.
+    int AddAgent(CrowdAgent* agent, const Vector3& pos);
+    /// Removes the detour crowd agent.
+    void RemoveAgent(CrowdAgent* agent);
+
+    /// Update the Navigation Agent's Avoidance Quality for the specified agent.
+    void UpdateAgentNavigationQuality(CrowdAgent* agent, NavigationQuality nq);
+    /// Update the Navigation Agent's Pushiness for the specified agent.
+    void UpdateAgentPushiness(CrowdAgent* agent, NavigationPushiness pushiness);
+
+    /// Set the move target for the specified agent.
+    bool SetAgentTarget(CrowdAgent* agent, Vector3 target);
+    /// Set the move target for the specified agent.
+    bool SetAgentTarget(CrowdAgent* agent, Vector3 target, unsigned int& targetRef);
+
+    /// Get the closest walkable position.
+    Vector3 GetClosestWalkablePosition(Vector3 pos) const;
+
+protected:
+    /// Update the crowd simulation.
+    void Update(float delta);
+    /// Handle node being assigned.
+    virtual void OnNodeSet(Node* node);
+    /// Get the detour crowd agent.
+    const dtCrowdAgent* GetCrowdAgent(int agent);
+    /// Get the internal detour crowd component.
+    dtCrowd* GetCrowd();
+
+private:
+    /// Handle the scene subsystem update event.
+    void HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData);
+    /// Handle full rebuilds of the navigation mesh.
+    void HandleNavMeshFullRebuild(StringHash eventType, VariantMap& eventData);
+
+    /// Internal crowd component.
+    dtCrowd* crowd_;
+    /// NavigationMesh for which the crowd was created.
+    WeakPtr<NavigationMesh> navigationMesh_;
+    /// Max agents for the crowd.
+    unsigned maxAgents_;    
+    /// Internal debug information.
+    dtCrowdAgentDebugInfo* agentDebug_;
+    /// Container for fetching agents from DetourCrowd during update.
+    PODVector<dtCrowdAgent*> agentBuffer_;
+    /// Container for fetching agents from DetourCrowd during update.
+    PODVector<CrowdAgent*> agents_;
+};
+
+}

+ 906 - 0
Source/Atomic/Navigation/DynamicNavigationMesh.cpp

@@ -0,0 +1,906 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../Navigation/DynamicNavigationMesh.h"
+
+#include "../Math/BoundingBox.h"
+#include "../Core/Context.h"
+#include "../Graphics/DebugRenderer.h"
+#include "../IO/Log.h"
+#include "../IO/MemoryBuffer.h"
+#include "../Navigation/NavBuildData.h"
+#include "../Navigation/NavigationEvents.h"
+#include "../Scene/Node.h"
+#include "../Navigation/OffMeshConnection.h"
+#include "../Core/Profiler.h"
+#include "../Navigation/Obstacle.h"
+#include "../Scene/Scene.h"
+#include "../Scene/SceneEvents.h"
+
+#include <LZ4/lz4.h>
+#include <cfloat>
+#include <Detour/DetourNavMesh.h>
+#include <Detour/DetourNavMeshBuilder.h>
+#include <Detour/DetourNavMeshQuery.h>
+#include <DetourTileCache/DetourTileCache.h>
+#include <DetourTileCache/DetourTileCacheBuilder.h>
+#include <Recast/Recast.h>
+#include <Recast/RecastAlloc.h>
+
+//DebugNew is deliberately not used because the macro 'free' conflicts DetourTileCache's LinearAllocator interface
+//#include "../DebugNew.h"
+
+#define TILECACHE_MAXLAYERS 128
+
+namespace Urho3D
+{
+    
+extern const char* NAVIGATION_CATEGORY;
+
+static const int DEFAULT_MAX_OBSTACLES = 1024;
+
+struct DynamicNavigationMesh::TileCacheData
+{
+    unsigned char* data;
+    int dataSize;
+};
+
+struct TileCompressor : public dtTileCacheCompressor
+{
+    virtual int maxCompressedSize(const int bufferSize)
+    {
+        return (int)(bufferSize* 1.05f);
+    }
+
+    virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
+        unsigned char* compressed, const int /*maxCompressedSize*/, int* compressedSize)
+    {
+        *compressedSize = LZ4_compress((const char*)buffer, (char*)compressed, bufferSize);
+        return DT_SUCCESS;
+    }
+
+    virtual dtStatus decompress(const unsigned char* compressed, const int compressedSize,
+        unsigned char* buffer, const int maxBufferSize, int* bufferSize)
+    {
+        *bufferSize = LZ4_decompress_safe((const char*)compressed, (char*)buffer, compressedSize, maxBufferSize);
+        return *bufferSize < 0 ? DT_FAILURE : DT_SUCCESS;
+    }
+};
+
+struct MeshProcess : public dtTileCacheMeshProcess
+{
+    DynamicNavigationMesh* owner_;
+    PODVector<Vector3> offMeshVertices_;
+    PODVector<float> offMeshRadii_;
+    PODVector<unsigned short> offMeshFlags_;
+    PODVector<unsigned char> offMeshAreas_;
+    PODVector<unsigned char> offMeshDir_;
+
+    inline MeshProcess(DynamicNavigationMesh* owner) :
+        owner_(owner)
+    {
+    }
+
+    virtual void process(struct dtNavMeshCreateParams* params, unsigned char* polyAreas, unsigned short* polyFlags)
+    {
+        // Update poly flags from areas.
+        // \todo Assignment of flags from areas?
+        for (int i = 0; i < params->polyCount; ++i)
+        {
+            if (polyAreas[i] != RC_NULL_AREA)
+                polyFlags[i] = RC_WALKABLE_AREA;
+        }
+
+        BoundingBox bounds;
+        rcVcopy(&bounds.min_.x_, params->bmin);
+        rcVcopy(&bounds.max_.x_, params->bmin);
+        
+        // collect off-mesh connections
+        PODVector<OffMeshConnection*> offMeshConnections = owner_->CollectOffMeshConnections(bounds);
+
+        if (offMeshConnections.Size() > 0)
+        {
+            if (offMeshConnections.Size() != offMeshRadii_.Size())
+            {
+                Matrix3x4 inverse = owner_->GetNode()->GetWorldTransform().Inverse();
+                ClearConnectionData();
+                for (unsigned i = 0; i < offMeshConnections.Size(); ++i)
+                {
+                    OffMeshConnection* connection = offMeshConnections[i];
+                    Vector3 start = inverse * connection->GetNode()->GetWorldPosition();
+                    Vector3 end = inverse * connection->GetEndPoint()->GetWorldPosition();
+
+                    offMeshVertices_.Push(start);
+                    offMeshVertices_.Push(end);
+                    offMeshRadii_.Push(connection->GetRadius());
+                    offMeshFlags_.Push(connection->GetMask());
+                    offMeshAreas_.Push((unsigned char)connection->GetAreaID());
+                    offMeshDir_.Push(connection->IsBidirectional() ? DT_OFFMESH_CON_BIDIR : 0);
+                }
+            }
+            params->offMeshConCount = offMeshRadii_.Size();
+            params->offMeshConVerts = &offMeshVertices_[0].x_;
+            params->offMeshConRad = &offMeshRadii_[0];
+            params->offMeshConFlags = &offMeshFlags_[0];
+            params->offMeshConAreas = &offMeshAreas_[0];
+            params->offMeshConDir = &offMeshDir_[0];
+        }
+    }
+
+    void ClearConnectionData()
+    {
+        offMeshVertices_.Clear();
+        offMeshRadii_.Clear();
+        offMeshFlags_.Clear();
+        offMeshAreas_.Clear();
+        offMeshDir_.Clear();
+    }
+};
+
+
+// From the Detour/Recast Sample_TempObstacles.cpp
+struct LinearAllocator : public dtTileCacheAlloc
+{
+    unsigned char* buffer;
+    int capacity;
+    int top;
+    int high;
+
+    LinearAllocator(const int cap) : buffer(0), capacity(0), top(0), high(0)
+    {
+        resize(cap);
+    }
+
+    ~LinearAllocator()
+    {
+        dtFree(buffer);
+    }
+
+    void resize(const int cap)
+    {
+        if (buffer) 
+            dtFree(buffer);
+        buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
+        capacity = cap;
+    }
+
+    virtual void reset()
+    {
+        high = Max(high, top);
+        top = 0;
+    }
+
+    virtual void* alloc(const int size)
+    {
+        if (!buffer)
+            return 0;
+        if (top + size > capacity)
+            return 0;
+        unsigned char* mem = &buffer[top];
+        top += size;
+        return mem;
+    }
+
+    virtual void free(void*)
+    {
+    }
+};
+
+
+DynamicNavigationMesh::DynamicNavigationMesh(Context* context) :
+    NavigationMesh(context),
+    tileCache_(0),
+    maxObstacles_(1024)
+{
+    //64 is the largest tile-size that DetourTileCache will tolerate without silently failing
+    tileSize_ = 64; 
+    partitionType_ = NAVMESH_PARTITION_MONOTONE;
+    allocator_ = new LinearAllocator(32000); //32kb to start
+    compressor_ = new TileCompressor();
+    meshProcessor_ = new MeshProcess(this);
+}
+
+DynamicNavigationMesh::~DynamicNavigationMesh()
+{
+    ReleaseNavigationMesh();
+    delete allocator_;
+    allocator_ = 0;
+    delete compressor_;
+    compressor_ = 0;
+    delete meshProcessor_;
+    meshProcessor_ = 0;
+}
+
+void DynamicNavigationMesh::RegisterObject(Context* context)
+{
+    context->RegisterFactory<DynamicNavigationMesh>(NAVIGATION_CATEGORY);
+
+    COPY_BASE_ATTRIBUTES(NavigationMesh);
+    ATTRIBUTE("Max Obstacles", unsigned, maxObstacles_, DEFAULT_MAX_OBSTACLES, AM_DEFAULT);
+}
+
+bool DynamicNavigationMesh::Build()
+{
+    PROFILE(BuildNavigationMesh);
+    // Release existing navigation data and zero the bounding box
+    ReleaseNavigationMesh();
+
+    if (!node_)
+        return false;
+
+    if (!node_->GetWorldScale().Equals(Vector3::ONE))
+        LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
+
+    Vector<NavigationGeometryInfo> geometryList;
+    CollectGeometries(geometryList);
+
+    if (geometryList.Empty())
+        return true; // Nothing to do
+
+    // Build the combined bounding box
+    for (unsigned i = 0; i < geometryList.Size(); ++i)
+        boundingBox_.Merge(geometryList[i].boundingBox_);
+
+    // Expand bounding box by padding
+    boundingBox_.min_ -= padding_;
+    boundingBox_.max_ += padding_;
+
+    {
+        PROFILE(BuildNavigationMesh);
+
+        // Calculate number of tiles
+        int gridW = 0, gridH = 0;
+        float tileEdgeLength = (float)tileSize_ * cellSize_;
+        rcCalcGridSize(&boundingBox_.min_.x_, &boundingBox_.max_.x_, cellSize_, &gridW, &gridH);
+        numTilesX_ = (gridW + tileSize_ - 1) / tileSize_;
+        numTilesZ_ = (gridH + tileSize_ - 1) / tileSize_;
+
+        // Calculate max. number of tiles and polygons, 22 bits available to identify both tile & polygon within tile
+        unsigned maxTiles = NextPowerOfTwo(numTilesX_ * numTilesZ_) * TILECACHE_MAXLAYERS;
+        unsigned tileBits = 0;
+        unsigned temp = maxTiles;
+        while (temp > 1)
+        {
+            temp >>= 1;
+            ++tileBits;
+        }
+
+        unsigned maxPolys = 1 << (22 - tileBits);
+
+        dtNavMeshParams params;
+        rcVcopy(params.orig, &boundingBox_.min_.x_);
+        params.tileWidth = tileEdgeLength;
+        params.tileHeight = tileEdgeLength;
+        params.maxTiles = maxTiles;
+        params.maxPolys = maxPolys;
+
+        navMesh_ = dtAllocNavMesh();
+        if (!navMesh_)
+        {
+            LOGERROR("Could not allocate navigation mesh");
+            return false;
+        }
+
+        if (dtStatusFailed(navMesh_->init(&params)))
+        {
+            LOGERROR("Could not initialize navigation mesh");
+            ReleaseNavigationMesh();
+            return false;
+        }
+
+        dtTileCacheParams tileCacheParams;
+        memset(&tileCacheParams, 0, sizeof(tileCacheParams));
+        rcVcopy(tileCacheParams.orig, &boundingBox_.min_.x_);
+        tileCacheParams.ch = cellHeight_;
+        tileCacheParams.cs = cellSize_;
+        tileCacheParams.width = tileSize_;
+        tileCacheParams.height = tileSize_;
+        tileCacheParams.maxSimplificationError = edgeMaxError_;
+        tileCacheParams.maxTiles = numTilesX_ * numTilesZ_ * TILECACHE_MAXLAYERS;
+        tileCacheParams.maxObstacles = maxObstacles_;
+        // Settings from NavigationMesh
+        tileCacheParams.walkableClimb = agentMaxClimb_;
+        tileCacheParams.walkableHeight = agentHeight_;
+        tileCacheParams.walkableRadius = agentRadius_;
+
+        tileCache_ = dtAllocTileCache();
+        if (!tileCache_)
+        {
+            LOGERROR("Could not allocate tile cache");
+            ReleaseNavigationMesh();
+            return false;
+        }
+
+        if (dtStatusFailed(tileCache_->init(&tileCacheParams, allocator_, compressor_, meshProcessor_)))
+        {
+            LOGERROR("Could not initialize tile cache");
+            ReleaseNavigationMesh();
+            return false;
+        }
+
+        // Build each tile
+        unsigned numTiles = 0;
+
+        for (int z = 0; z < numTilesZ_; ++z)
+        {
+            for (int x = 0; x < numTilesX_; ++x)
+            {
+                TileCacheData tiles[TILECACHE_MAXLAYERS];
+                int layerCt = BuildTile(geometryList, x, z, tiles);
+                for (int i = 0; i < layerCt; ++i)
+                {
+                    dtCompressedTileRef tileRef;
+                    int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef);
+                    if (dtStatusFailed(status))
+                    {
+                        dtFree(tiles[i].data);
+                        tiles[i].data = 0x0;
+                    }
+                }
+                ++numTiles;
+            }
+            for (int x = 0; x < numTilesX_; ++x)
+                tileCache_->buildNavMeshTilesAt(x, z, navMesh_);
+        }
+        
+        // For a full build it's necessary to update the nav mesh
+        // not doing so will cause dependent components to crash, like DetourCrowdManager
+        tileCache_->update(0, navMesh_);
+
+        LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles");
+
+        // Send a notification event to concerned parties that we've been fully rebuilt
+        {
+            using namespace NavigationMeshRebuilt;
+            VariantMap& buildEventParams = GetContext()->GetEventDataMap();
+            buildEventParams[P_NODE] = node_;
+            buildEventParams[P_MESH] = this;
+            SendEvent(E_NAVIGATION_MESH_REBUILT, buildEventParams);
+        }
+
+        // Scan for obstacles to insert into us
+        PODVector<Node*> obstacles;
+        GetScene()->GetChildrenWithComponent<Obstacle>(obstacles, true);
+        for (unsigned i = 0; i < obstacles.Size(); ++i)
+        {
+            Obstacle* obs = obstacles[i]->GetComponent<Obstacle>();
+            if (obs && obs->IsEnabledEffective())
+                AddObstacle(obs);
+        }
+
+        return true;
+    }
+}
+
+bool DynamicNavigationMesh::Build(const BoundingBox& boundingBox)
+{
+    PROFILE(BuildPartialNavigationMesh);
+
+    if (!node_)
+        return false;
+
+    if (!navMesh_)
+    {
+        LOGERROR("Navigation mesh must first be built fully before it can be partially rebuilt");
+        return false;
+    }
+
+    if (!node_->GetWorldScale().Equals(Vector3::ONE))
+        LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
+
+    BoundingBox localSpaceBox = boundingBox.Transformed(node_->GetWorldTransform().Inverse());
+
+    float tileEdgeLength = (float)tileSize_ * cellSize_;
+
+    Vector<NavigationGeometryInfo> geometryList;
+    CollectGeometries(geometryList);
+
+    int sx = Clamp((int)((localSpaceBox.min_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1);
+    int sz = Clamp((int)((localSpaceBox.min_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1);
+    int ex = Clamp((int)((localSpaceBox.max_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1);
+    int ez = Clamp((int)((localSpaceBox.max_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1);
+
+    unsigned numTiles = 0;
+
+    for (int z = sz; z <= ez; ++z)
+    {
+        for (int x = sx; x <= ex; ++x)
+        {
+            dtCompressedTileRef existing[TILECACHE_MAXLAYERS];
+            const int existingCt = tileCache_->getTilesAt(x, z, existing, TILECACHE_MAXLAYERS);
+            for (int i = 0; i < existingCt; ++i)
+            {
+                unsigned char* data = 0x0;
+                if (!dtStatusFailed(tileCache_->removeTile(existing[i], &data, 0)) && data != 0x0)
+                    dtFree(data);
+            }
+
+            TileCacheData tiles[TILECACHE_MAXLAYERS];
+            int layerCt = BuildTile(geometryList, x, z, tiles);
+            for (int i = 0; i < layerCt; ++i)
+            {
+                dtCompressedTileRef tileRef;
+                int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef);
+                if (dtStatusFailed(status))
+                {
+                    dtFree(tiles[i].data);
+                    tiles[i].data = 0x0;
+                }
+                else
+                {
+                    tileCache_->buildNavMeshTile(tileRef, navMesh_);
+                    ++numTiles;
+                }
+            }
+        }
+    }
+
+    LOGDEBUG("Rebuilt " + String(numTiles) + " tiles of the navigation mesh");
+    return true;
+}
+
+
+void DynamicNavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (!debug || !navMesh_ || !node_)
+        return;
+
+    const Matrix3x4& worldTransform = node_->GetWorldTransform();
+
+    const dtNavMesh* navMesh = navMesh_;
+
+    for (int z = 0; z < numTilesZ_; ++z)
+    {
+        for (int x = 0; x < numTilesX_; ++x)
+        {
+            // Get the layers from the tile-cache
+            const dtMeshTile* tiles[TILECACHE_MAXLAYERS];
+            int tileCount = navMesh->getTilesAt(x, z, tiles, TILECACHE_MAXLAYERS);
+            for (int i = 0; i < tileCount; ++i) 
+            {
+                const dtMeshTile* tile = tiles[i];
+                if (!tile)
+                    continue;
+
+                for (int i = 0; i < tile->header->polyCount; ++i)
+                {
+                    dtPoly* poly = tile->polys + i;
+                    for (unsigned j = 0; j < poly->vertCount; ++j)
+                    {
+                        debug->AddLine(
+                            worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[j] * 3]),
+                            worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[(j + 1) % poly->vertCount] * 3]),
+                            Color::YELLOW,
+                            depthTest
+                            );
+                    }
+                }
+            }
+        }
+    }
+}
+
+void DynamicNavigationMesh::SetNavigationDataAttr(const PODVector<unsigned char>& value)
+{
+    ReleaseNavigationMesh();
+        
+    if (value.Empty())
+        return;
+
+    MemoryBuffer buffer(value);
+    boundingBox_ = buffer.ReadBoundingBox();
+    numTilesX_ = buffer.ReadInt();
+    numTilesZ_ = buffer.ReadInt();
+
+    dtNavMeshParams params;
+    buffer.Read(&params, sizeof(dtNavMeshParams));
+
+    navMesh_ = dtAllocNavMesh();
+    if (!navMesh_)
+    {
+        LOGERROR("Could not allocate navigation mesh");
+        return;
+    }
+
+    if (dtStatusFailed(navMesh_->init(&params)))
+    {
+        LOGERROR("Could not initialize navigation mesh");
+        ReleaseNavigationMesh();
+        return;
+    }
+
+    dtTileCacheParams tcParams;
+    buffer.Read(&tcParams, sizeof(tcParams));
+
+    tileCache_ = dtAllocTileCache();
+    if (!tileCache_)
+    {
+        LOGERROR("Could not allocate tile cache");
+        ReleaseNavigationMesh();
+        return;
+    }
+    if (dtStatusFailed(tileCache_->init(&tcParams, allocator_, compressor_, meshProcessor_)))
+    {
+        LOGERROR("Could not initialize tile cache");
+        ReleaseNavigationMesh();
+        return;
+    }
+
+    while (!buffer.IsEof())
+    {
+        dtTileCacheLayerHeader header;
+        buffer.Read(&header, sizeof(dtTileCacheLayerHeader));
+        const int dataSize = buffer.ReadInt();
+        unsigned char* data = (unsigned char*)dtAlloc(dataSize, DT_ALLOC_PERM);
+        buffer.Read(data, dataSize);
+
+        if (dtStatusFailed(tileCache_->addTile(data, dataSize, DT_TILE_FREE_DATA, 0)))
+        {
+            LOGERROR("Failed to add tile");
+            dtFree(data);
+            return;
+        }
+    }
+
+    for (int x = 0; x < numTilesX_; ++x)
+    {
+        for (int z = 0; z < numTilesZ_; ++z)
+            tileCache_->buildNavMeshTilesAt(x, z, navMesh_);
+    }
+
+    tileCache_->update(0, navMesh_);
+}
+
+PODVector<unsigned char> DynamicNavigationMesh::GetNavigationDataAttr() const
+{
+    VectorBuffer ret;
+    if (navMesh_ && tileCache_)
+    {
+        ret.WriteBoundingBox(boundingBox_);
+        ret.WriteInt(numTilesX_);
+        ret.WriteInt(numTilesZ_);
+
+        const dtNavMeshParams* params = navMesh_->getParams();
+        ret.Write(params, sizeof(dtNavMeshParams));
+
+        const dtTileCacheParams* tcParams = tileCache_->getParams();
+        ret.Write(tcParams, sizeof(dtTileCacheParams));
+
+        for (int z = 0; z < numTilesZ_; ++z)
+        {
+            for (int x = 0; x < numTilesX_; ++x)
+            {
+                dtCompressedTileRef tiles[TILECACHE_MAXLAYERS];
+                const int ct = tileCache_->getTilesAt(x, z, tiles, TILECACHE_MAXLAYERS);
+                for (int i = 0; i < ct; ++i)
+                {
+                    const dtCompressedTile* tile = tileCache_->getTileByRef(tiles[i]);
+                    if (!tile || !tile->header || !tile->dataSize)
+                        continue; // Don't write "void-space" tiles
+                    // The header conveniently has the majority of the information required
+                    ret.Write(tile->header, sizeof(dtTileCacheLayerHeader));
+                    ret.WriteInt(tile->dataSize);
+                    ret.Write(tile->data, tile->dataSize);
+                }
+            }
+        }
+    }
+    return ret.GetBuffer();
+}
+
+int DynamicNavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int x, int z, TileCacheData* tiles)
+{
+    PROFILE(BuildNavigationMeshTile);
+
+    tileCache_->removeTile(navMesh_->getTileRefAt(x, z, 0), 0, 0);
+
+    float tileEdgeLength = (float)tileSize_ * cellSize_;
+
+    BoundingBox tileBoundingBox(Vector3(
+        boundingBox_.min_.x_ + tileEdgeLength * (float)x,
+        boundingBox_.min_.y_,
+        boundingBox_.min_.z_ + tileEdgeLength * (float)z
+        ),
+        Vector3(
+        boundingBox_.min_.x_ + tileEdgeLength * (float)(x + 1),
+        boundingBox_.max_.y_,
+        boundingBox_.min_.z_ + tileEdgeLength * (float)(z + 1)
+        ));
+
+    DynamicNavBuildData build(allocator_);
+
+    rcConfig cfg;
+    memset(&cfg, 0, sizeof cfg);
+    cfg.cs = cellSize_;
+    cfg.ch = cellHeight_;
+    cfg.walkableSlopeAngle = agentMaxSlope_;
+    cfg.walkableHeight = (int)ceilf(agentHeight_ / cfg.ch);
+    cfg.walkableClimb = (int)floorf(agentMaxClimb_ / cfg.ch);
+    cfg.walkableRadius = (int)ceilf(agentRadius_ / cfg.cs);
+    cfg.maxEdgeLen = (int)(edgeMaxLength_ / cellSize_);
+    cfg.maxSimplificationError = edgeMaxError_;
+    cfg.minRegionArea = (int)sqrtf(regionMinSize_);
+    cfg.mergeRegionArea = (int)sqrtf(regionMergeSize_);
+    cfg.maxVertsPerPoly = 6;
+    cfg.tileSize = tileSize_;
+    cfg.borderSize = cfg.walkableRadius + 3; // Add padding
+    cfg.width = cfg.tileSize + cfg.borderSize * 2;
+    cfg.height = cfg.tileSize + cfg.borderSize * 2;
+    cfg.detailSampleDist = detailSampleDistance_ < 0.9f ? 0.0f : cellSize_ * detailSampleDistance_;
+    cfg.detailSampleMaxError = cellHeight_ * detailSampleMaxError_;
+
+    rcVcopy(cfg.bmin, &tileBoundingBox.min_.x_);
+    rcVcopy(cfg.bmax, &tileBoundingBox.max_.x_);
+    cfg.bmin[0] -= cfg.borderSize * cfg.cs;
+    cfg.bmin[2] -= cfg.borderSize * cfg.cs;
+    cfg.bmax[0] += cfg.borderSize * cfg.cs;
+    cfg.bmax[2] += cfg.borderSize * cfg.cs;
+
+    BoundingBox expandedBox(*reinterpret_cast<Vector3*>(cfg.bmin), *reinterpret_cast<Vector3*>(cfg.bmax));
+    GetTileGeometry(&build, geometryList, expandedBox);
+
+    if (build.vertices_.Empty() || build.indices_.Empty())
+        return 0; // Nothing to do
+
+    build.heightField_ = rcAllocHeightfield();
+    if (!build.heightField_)
+    {
+        LOGERROR("Could not allocate heightfield");
+        return 0;
+    }
+
+    if (!rcCreateHeightfield(build.ctx_, *build.heightField_, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs,
+        cfg.ch))
+    {
+        LOGERROR("Could not create heightfield");
+        return 0;
+    }
+
+    unsigned numTriangles = build.indices_.Size() / 3;
+    SharedArrayPtr<unsigned char> triAreas(new unsigned char[numTriangles]);
+    memset(triAreas.Get(), 0, numTriangles);
+
+    rcMarkWalkableTriangles(build.ctx_, cfg.walkableSlopeAngle, &build.vertices_[0].x_, build.vertices_.Size(),
+        &build.indices_[0], numTriangles, triAreas.Get());
+    rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
+        triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
+    rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
+    
+    rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
+    rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
+
+    build.compactHeightField_ = rcAllocCompactHeightfield();
+    if (!build.compactHeightField_)
+    {
+        LOGERROR("Could not allocate create compact heightfield");
+        return 0;
+    }
+    if (!rcBuildCompactHeightfield(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_,
+        *build.compactHeightField_))
+    {
+        LOGERROR("Could not build compact heightfield");
+        return 0;
+    }
+    if (!rcErodeWalkableArea(build.ctx_, cfg.walkableRadius, *build.compactHeightField_))
+    {
+        LOGERROR("Could not erode compact heightfield");
+        return 0;
+    }
+
+    // area volumes
+    for (unsigned i = 0; i < build.navAreas_.Size(); ++i)
+        rcMarkBoxArea(build.ctx_, &build.navAreas_[i].bounds_.min_.x_, &build.navAreas_[i].bounds_.max_.x_, build.navAreas_[i].areaID_, *build.compactHeightField_);
+
+    if (this->partitionType_ == NAVMESH_PARTITION_WATERSHED)
+    {
+        if (!rcBuildDistanceField(build.ctx_, *build.compactHeightField_))
+        {
+            LOGERROR("Could not build distance field");
+            return 0;
+        }
+        if (!rcBuildRegions(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea,
+            cfg.mergeRegionArea))
+        {
+            LOGERROR("Could not build regions");
+            return 0;
+        }
+    }
+    else
+    {
+        if (!rcBuildRegionsMonotone(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea))
+        {
+            LOGERROR("Could not build monotone regions");
+            return 0;
+        }
+    }
+    
+    build.heightFieldLayers_ = rcAllocHeightfieldLayerSet();
+    if (!build.heightFieldLayers_)
+    {
+        LOGERROR("Could not allocate height field layer set");
+        return 0;
+    }
+
+    if (!rcBuildHeightfieldLayers(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.walkableHeight, *build.heightFieldLayers_))
+    {
+        LOGERROR("Could not build height field layers");
+        return 0;
+    }
+
+    int retCt = 0;
+    for (int i = 0; i < build.heightFieldLayers_->nlayers; ++i)
+    {
+        dtTileCacheLayerHeader header;
+        header.magic = DT_TILECACHE_MAGIC;
+        header.version = DT_TILECACHE_VERSION;
+        header.tx = x;
+        header.ty = z;
+        header.tlayer = i;
+
+        rcHeightfieldLayer* layer = &build.heightFieldLayers_->layers[i];
+
+        // Tile info.
+        rcVcopy(header.bmin, layer->bmin);
+        rcVcopy(header.bmax, layer->bmax);
+        header.width = (unsigned char)layer->width;
+        header.height = (unsigned char)layer->height;
+        header.minx = (unsigned char)layer->minx;
+        header.maxx = (unsigned char)layer->maxx;
+        header.miny = (unsigned char)layer->miny;
+        header.maxy = (unsigned char)layer->maxy;
+        header.hmin = (unsigned short)layer->hmin;
+        header.hmax = (unsigned short)layer->hmax;
+
+        if (dtStatusFailed(dtBuildTileCacheLayer(compressor_/*compressor*/, &header, layer->heights, layer->areas/*areas*/, layer->cons, &(tiles[retCt].data), &tiles[retCt].dataSize)))
+        {
+            LOGERROR("Failed to build tile cache layers");
+            return 0;
+        }
+        else
+            ++retCt;
+    }
+
+    // Send a notification of the rebuild of this tile to anyone interested
+    {
+        using namespace NavigationAreaRebuilt;
+        VariantMap& eventData = GetContext()->GetEventDataMap();
+        eventData[P_NODE] = GetNode();
+        eventData[P_MESH] = this;
+        eventData[P_BOUNDSMIN] = Variant(tileBoundingBox.min_);
+        eventData[P_BOUNDSMAX] = Variant(tileBoundingBox.max_);
+        SendEvent(E_NAVIGATION_AREA_REBUILT, eventData);
+    }
+
+    return retCt;
+}
+
+PODVector<OffMeshConnection*> DynamicNavigationMesh::CollectOffMeshConnections(const BoundingBox& bounds)
+{
+    PODVector<OffMeshConnection*> connections;
+    node_->GetComponents<OffMeshConnection>(connections, true);
+    for (unsigned i = 0; i < connections.Size(); ++i)
+    {
+        OffMeshConnection* connection = connections[i];
+        if (!(connection->IsEnabledEffective() && connection->GetEndPoint()))
+        {
+            // discard this connection
+            connections.Erase(i);
+            --i;
+        }
+    }
+
+    return connections;
+}
+
+void DynamicNavigationMesh::ReleaseNavigationMesh()
+{
+    NavigationMesh::ReleaseNavigationMesh();
+    ReleaseTileCache();
+}
+
+void DynamicNavigationMesh::ReleaseTileCache()
+{
+    dtFreeTileCache(tileCache_);
+    tileCache_ = 0;
+}
+
+void DynamicNavigationMesh::OnNodeSet(Node* node)
+{
+    // Subscribe to the scene subsystem update, which will trigger the tile cache to update the nav mesh
+    if (node)
+        SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(DynamicNavigationMesh, HandleSceneSubsystemUpdate));
+}
+
+void DynamicNavigationMesh::AddObstacle(Obstacle* obstacle, bool silent)
+{
+    if (tileCache_)
+    {
+        float pos[3];
+        Vector3 obsPos = obstacle->GetNode()->GetWorldPosition();
+        rcVcopy(pos, &obsPos.x_);
+        dtObstacleRef refHolder;
+        if (dtStatusFailed(tileCache_->addObstacle(pos, obstacle->GetRadius(), obstacle->GetHeight(), &refHolder)))
+        {
+            LOGERROR("Failed to add obstacle");
+            return;
+        }
+        obstacle->obstacleId_ = refHolder;
+        assert(refHolder > 0);
+        tileCache_->update(1, navMesh_);
+        
+        if (!silent)
+        {
+            using namespace NavigationObstacleAdded;
+            VariantMap& eventData = GetContext()->GetEventDataMap();
+            eventData[P_NODE] = obstacle->GetNode();
+            eventData[P_OBSTACLE] = obstacle;
+            eventData[P_POSITION] = obstacle->GetNode()->GetWorldPosition();
+            eventData[P_RADIUS] = obstacle->GetRadius();
+            eventData[P_HEIGHT] = obstacle->GetHeight();
+            SendEvent(E_NAVIGATION_OBSTACLE_ADDED, eventData);
+        }
+    }
+}
+
+void DynamicNavigationMesh::ObstacleChanged(Obstacle* obstacle)
+{
+    if (tileCache_)
+    {
+        RemoveObstacle(obstacle, true);
+        AddObstacle(obstacle, true);
+    }
+}
+
+void DynamicNavigationMesh::RemoveObstacle(Obstacle* obstacle, bool silent)
+{
+    if (tileCache_ && obstacle->obstacleId_ > 0)
+    {
+        if (dtStatusFailed(tileCache_->removeObstacle(obstacle->obstacleId_)))
+        {
+            LOGERROR("Failed to remove obstacle");
+            return;
+        }
+        // Require a node in order to send an event
+        if (!silent && obstacle->GetNode())
+        {
+            obstacle->obstacleId_ = 0;
+            using namespace NavigationObstacleRemoved;
+            VariantMap& eventData = GetContext()->GetEventDataMap();
+            eventData[P_NODE] = obstacle->GetNode();
+            eventData[P_OBSTACLE] = obstacle;
+            eventData[P_POSITION] = obstacle->GetNode()->GetWorldPosition();
+            eventData[P_RADIUS] = obstacle->GetRadius();
+            eventData[P_HEIGHT] = obstacle->GetHeight();
+            SendEvent(E_NAVIGATION_OBSTACLE_ADDED, eventData);
+        }
+    }
+}
+
+void DynamicNavigationMesh::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
+{
+    using namespace SceneSubsystemUpdate;
+
+    if (tileCache_ && navMesh_ && IsEnabledEffective())
+        tileCache_->update(eventData[P_TIMESTEP].GetFloat(), navMesh_);
+}
+
+}

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

@@ -0,0 +1,106 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Navigation/NavigationMesh.h"
+
+class dtTileCache;
+struct dtTileCacheAlloc;
+struct dtTileCacheCompressor;
+struct dtTileCacheMeshProcess;
+struct dtTileCacheLayer;
+struct dtTileCacheContourSet;
+struct dtTileCachePolyMesh;
+
+namespace Urho3D
+{
+
+class OffMeshConnection;
+class Obstacle;
+
+class URHO3D_API DynamicNavigationMesh : public NavigationMesh
+{
+    OBJECT(DynamicNavigationMesh)
+    friend class Obstacle;
+    friend struct MeshProcess;
+
+public:
+    /// Constructor.
+    DynamicNavigationMesh(Context*);
+    /// Destructor.
+    virtual ~DynamicNavigationMesh();
+
+    /// Register with engine context.
+    static void RegisterObject(Context*);
+
+    /// Build/rebuild the entire navigation mesh.
+    virtual bool Build();
+    /// Build/rebuild a portion of the navigation mesh.
+    virtual bool Build(const BoundingBox& boundingBox);
+    /// Visualize the component as debug geometry.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+
+    /// Set navigation data attribute.
+    virtual void SetNavigationDataAttr(const PODVector<unsigned char>& value);
+    /// Return navigation data attribute.
+    virtual PODVector<unsigned char> GetNavigationDataAttr() const;
+
+protected:
+    struct TileCacheData;
+
+    /// Subscribe to events when assigned to a node.
+    virtual void OnNodeSet(Node*);
+    /// Trigger the tile cache to make updates to the nav mesh if necessary.
+    void HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData);
+
+    /// Used by Obstacle class to add itself to the tile cache, if 'silent' an event will not be raised.
+    void AddObstacle(Obstacle* obstacle, bool silent = false);
+    /// Used by Obstacle class to update itself.
+    void ObstacleChanged(Obstacle* obstacle);
+    /// Used by Obstacle class to remove itself from the tile cache, if 'silent' an event will not be raised.
+    void RemoveObstacle(Obstacle*, bool silent = false);
+
+    /// Build one tile of the navigation mesh. Return true if successful.
+    int BuildTile(Vector<NavigationGeometryInfo>& geometryList, int x, int z, TileCacheData*);
+    /// Off-mesh connections to be rebuilt in the mesh processor.
+    PODVector<OffMeshConnection*> CollectOffMeshConnections(const BoundingBox& bounds);
+    /// Release the navigation mesh, query, and tile cache.
+    virtual void ReleaseNavigationMesh();
+
+private:
+    /// Free the tile cache.
+    void ReleaseTileCache();
+
+    /// Detour tile cache instance that works with the nav mesh.
+    dtTileCache* tileCache_;
+    /// Used by dtTileCache to allocate blocks of memory.
+    dtTileCacheAlloc* allocator_;
+    /// Used by dtTileCache to compress the original tiles to use when reconstructing for changes.
+    dtTileCacheCompressor* compressor_;
+    /// Mesh processer used by Detour, in this case a 'pass-through' processor.
+    dtTileCacheMeshProcess* meshProcessor_;
+    /// Maximum number of obstacle objects allowed.
+    unsigned maxObstacles_;
+};
+
+}

+ 86 - 0
Source/Atomic/Navigation/NavArea.cpp

@@ -0,0 +1,86 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../Scene/Component.h"
+#include "../Core/Context.h"
+#include "../Graphics/DebugRenderer.h"
+#include "../IO/Log.h"
+#include "../Navigation/NavArea.h"
+#include "../Scene/Node.h"
+#include "../Container/Str.h"
+
+namespace Urho3D
+{
+    static const unsigned MAX_NAV_AREA_ID = 255;
+    static const Vector3 DEFAULT_BOUNDING_BOX_MIN(-10.0f, -10.0f, -10.0f);
+    static const Vector3 DEFAULT_BOUNDING_BOX_MAX(10.0f, 10.0f, 10.0f);
+    static const unsigned DEFAULT_MASK_FLAG = 0;
+    static const unsigned DEFAULT_AREA_ID = 0;
+
+    extern const char* NAVIGATION_CATEGORY;
+
+    NavArea::NavArea(Context* context) :
+        Component(context),
+        areaID_(DEFAULT_AREA_ID),
+        boundingBox_(DEFAULT_BOUNDING_BOX_MIN, DEFAULT_BOUNDING_BOX_MAX)
+    {
+    }
+
+    NavArea::~NavArea()
+    {
+    }
+    
+    void NavArea::RegisterObject(Context* context)
+    {
+        context->RegisterFactory<NavArea>(NAVIGATION_CATEGORY);
+
+        COPY_BASE_ATTRIBUTES(Component);
+        ATTRIBUTE("Bounding Box Min", Vector3, boundingBox_.min_, DEFAULT_BOUNDING_BOX_MIN, AM_DEFAULT);
+        ATTRIBUTE("Bounding Box Max", Vector3, boundingBox_.max_, DEFAULT_BOUNDING_BOX_MAX, AM_DEFAULT);
+        ACCESSOR_ATTRIBUTE("Area ID", GetAreaID, SetAreaID, unsigned, DEFAULT_AREA_ID, AM_DEFAULT);
+    }
+
+    void NavArea::SetAreaID(unsigned newID)
+    {
+        if (newID > MAX_NAV_AREA_ID)
+            LOGERRORF("NavArea Area ID %u exceeds maximum value of %u", newID, MAX_NAV_AREA_ID);
+        areaID_ = (unsigned char)newID;
+        MarkNetworkUpdate();
+    }
+
+    BoundingBox NavArea::GetWorldBoundingBox() const
+    {
+        Matrix3x4 mat;
+        mat.SetTranslation(node_->GetWorldPosition());
+        return boundingBox_.Transformed(mat);
+    }
+
+    void NavArea::DrawDebugGeometry(DebugRenderer* debug, bool depthTest) 
+    {
+        if (debug && IsEnabledEffective())
+        {
+            Matrix3x4 mat;
+            mat.SetTranslation(node_->GetWorldPosition());
+            debug->AddBoundingBox(boundingBox_, mat, Color::GREEN, depthTest);
+        }
+    }
+}

+ 66 - 0
Source/Atomic/Navigation/NavArea.h

@@ -0,0 +1,66 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Scene/Component.h"
+#include "../Math/BoundingBox.h"
+
+namespace Urho3D
+{
+    class URHO3D_API NavArea : public Component
+    {
+        OBJECT(NavArea);
+
+    public:
+        /// Construct.
+        NavArea(Context*);
+        /// Destruct.
+        virtual ~NavArea();
+        /// Register object factory and attributes.
+        static void RegisterObject(Context*);
+
+        /// Render debug geometry for the bounds.
+        virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+
+        /// Get the area id for this volume.
+        unsigned GetAreaID() const { return (unsigned)areaID_; }
+        /// Set the area id for this volume.
+        void SetAreaID(unsigned newID);
+
+        /// Get the bounding box of this navigation area, in local space.
+        BoundingBox GetBoundingBox() const { return boundingBox_; }
+        /// Set the bounding box of this area, in local space.
+        void SetBoundingBox(const BoundingBox& bnds) { boundingBox_ = bnds; }
+
+        /// Get the bounds of this navigation area in world space.
+        BoundingBox GetWorldBoundingBox() const;
+
+    private:
+        /// Bounds of area to mark.
+        BoundingBox boundingBox_;
+        /// Flags to assign to the marked area of the navigation map.
+        unsigned flags_;
+        /// Area id to assign to the marked area.
+        unsigned char areaID_;
+    };
+}

+ 96 - 0
Source/Atomic/Navigation/NavBuildData.cpp

@@ -0,0 +1,96 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../Navigation/NavBuildData.h"
+
+#include <Recast/Recast.h>
+#include <Detour/DetourNavMesh.h>
+#include <Detour/DetourNavMeshBuilder.h>
+#include <Detour/DetourNavMeshQuery.h>
+#include <DetourTileCache/DetourTileCache.h>
+#include <DetourTileCache/DetourTileCacheBuilder.h>
+
+namespace Urho3D
+{
+
+NavBuildData::NavBuildData() :
+    ctx_(0),
+    heightField_(0),
+    compactHeightField_(0)
+{
+    ctx_ = new rcContext(true);
+}
+
+NavBuildData::~NavBuildData()
+{
+    if (ctx_)
+        delete(ctx_);
+    rcFreeHeightField(heightField_);
+    rcFreeCompactHeightfield(compactHeightField_);
+
+    ctx_ = 0;
+    heightField_ = 0;
+    compactHeightField_ = 0;
+}
+
+SimpleNavBuildData::SimpleNavBuildData() :
+    NavBuildData(),
+    contourSet_(0),
+    polyMesh_(0),
+    polyMeshDetail_(0)
+{
+}
+
+SimpleNavBuildData::~SimpleNavBuildData()
+{
+    rcFreeContourSet(contourSet_);
+    rcFreePolyMesh(polyMesh_);
+    rcFreePolyMeshDetail(polyMeshDetail_);
+
+    contourSet_ = 0;
+    polyMesh_ = 0;
+    polyMeshDetail_ = 0;
+}
+
+DynamicNavBuildData::DynamicNavBuildData(dtTileCacheAlloc* allocator) :
+    contourSet_(0),
+    heightFieldLayers_(0),
+    polyMesh_(0),
+    alloc_(allocator)
+{
+}
+
+DynamicNavBuildData::~DynamicNavBuildData()
+{
+    if (contourSet_)
+        dtFreeTileCacheContourSet(alloc_, contourSet_);
+    if (polyMesh_)
+        dtFreeTileCachePolyMesh(alloc_, polyMesh_);
+    if (heightFieldLayers_)
+        rcFreeHeightfieldLayerSet(heightFieldLayers_);
+
+    contourSet_ = 0;
+    polyMesh_ = 0;
+    heightFieldLayers_ = 0;
+}
+
+}

+ 119 - 0
Source/Atomic/Navigation/NavBuildData.h

@@ -0,0 +1,119 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Container/Vector.h"
+#include "../Math/BoundingBox.h"
+#include "../Math/Vector3.h"
+
+class rcContext;
+struct rcHeightfield;
+struct rcCompactHeightfield;
+struct rcContourSet;
+struct rcPolyMesh;
+struct rcPolyMeshDetail;
+
+struct rcHeightfieldLayerSet;
+struct dtTileCacheContourSet;
+struct dtTileCachePolyMesh;
+struct dtTileCacheAlloc;
+
+namespace Urho3D
+{
+
+/// Navigation area stub.
+struct URHO3D_API NavAreaStub
+{
+    /// Area bounding box.
+    BoundingBox bounds_;
+    /// Area ID.
+    unsigned char areaID_;
+};
+
+/// Navigation build data.
+struct URHO3D_API NavBuildData
+{
+    /// Constructor.
+    NavBuildData();
+    /// Destructor.
+    virtual ~NavBuildData();
+
+    /// World-space bounding box of the navigation mesh tile.
+    BoundingBox worldBoundingBox_;
+    /// Vertices from geometries.
+    PODVector<Vector3> vertices_;
+    /// Triangle indices from geometries.
+    PODVector<int> indices_;
+    /// Offmesh connection vertices.
+    PODVector<Vector3> offMeshVertices_;
+    /// Offmesh connection radii.
+    PODVector<float> offMeshRadii_;
+    /// Offmesh connection flags.
+    PODVector<unsigned short> offMeshFlags_;
+    /// Offmesh connection areas.
+    PODVector<unsigned char> offMeshAreas_;
+    /// Offmesh connection direction.
+    PODVector<unsigned char> offMeshDir_;
+    /// Recast context.
+    rcContext* ctx_;
+    /// Recast heightfield.
+    rcHeightfield* heightField_;
+    /// Recast compact heightfield.
+    rcCompactHeightfield* compactHeightField_;
+    /// Pretransformed navigation areas, no correlation to the geometry above.
+    PODVector<NavAreaStub> navAreas_;
+};
+
+struct SimpleNavBuildData : public NavBuildData
+{
+    /// Constructor.
+    SimpleNavBuildData();
+    /// Descturctor.
+    virtual ~SimpleNavBuildData();
+
+    /// Recast contour set.
+    rcContourSet* contourSet_;
+    /// Recast poly mesh.
+    rcPolyMesh* polyMesh_;
+    /// Recast detail poly mesh.
+    rcPolyMeshDetail* polyMeshDetail_;
+};
+
+struct DynamicNavBuildData : public NavBuildData
+{
+    /// Constructor.
+    DynamicNavBuildData(dtTileCacheAlloc* alloc);
+    /// Destructor.
+    virtual ~DynamicNavBuildData();
+
+    /// TileCache specific recast contour set.
+    dtTileCacheContourSet* contourSet_;
+    /// TileCache specific recast poly mesh.
+    dtTileCachePolyMesh* polyMesh_;
+    /// Recast heightfield layer set.
+    rcHeightfieldLayerSet* heightFieldLayers_;
+    /// Allocator from DynamicNavigationMesh instance.
+    dtTileCacheAlloc* alloc_;
+};
+
+}

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

@@ -0,0 +1,97 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Core/Object.h"
+
+namespace Urho3D
+{
+
+/// Complete rebuild of navigation mesh.
+EVENT(E_NAVIGATION_MESH_REBUILT, NavigationMeshRebuilt)
+{
+    PARAM(P_NODE, Node); // Node pointer
+    PARAM(P_MESH, Mesh); // NavigationMesh pointer
+}
+
+/// Partial bounding box rebuild of navigation mesh.
+EVENT(E_NAVIGATION_AREA_REBUILT, NavigationAreaRebuilt)
+{
+    PARAM(P_NODE, Node); // Node pointer
+    PARAM(P_MESH, Mesh); // NavigationMesh pointer
+    PARAM(P_BOUNDSMIN, BoundsMin); // Vector3
+    PARAM(P_BOUNDSMAX, BoundsMax); // Vector3
+}
+
+/// Crowd agent has been repositioned.
+EVENT(E_CROWD_AGENT_REPOSITION, CrowdAgentReposition)
+{
+    PARAM(P_NODE, Node); // Node pointer
+    PARAM(P_CROWD_AGENT, CrowdAgent); // CrowdAgent pointer
+    PARAM(P_POSITION, Position); // Vector3
+    PARAM(P_VELOCITY, Velocity); // Vector3
+}
+
+/// Crowd agent's internal state has become invalidated.
+EVENT(E_CROWD_AGENT_FAILURE, CrowdAgentFailure)
+{
+    PARAM(P_NODE, Node); // Node pointer
+    PARAM(P_CROWD_AGENT, CrowdAgent); // CrowdAgent pointer
+    PARAM(P_POSITION, Position); // Vector3
+    PARAM(P_VELOCITY, Velocity); // Vector3
+    PARAM(P_CROWD_AGENT_STATE, CrowdAgentState); // int
+    PARAM(P_CROWD_TARGET_STATE, CrowdTargetState); // int
+}
+
+/// Crowd agent's state has been changed.
+EVENT(E_CROWD_AGENT_STATE_CHANGED, CrowdAgentStateChanged)
+{
+    PARAM(P_NODE, Node); // Node pointer
+    PARAM(P_CROWD_AGENT, CrowdAgent); // CrowdAgent pointer
+    PARAM(P_POSITION, Position); // Vector3
+    PARAM(P_VELOCITY, Velocity); // Vector3
+    PARAM(P_CROWD_AGENT_STATE, CrowdAgentState); // int
+    PARAM(P_CROWD_TARGET_STATE, CrowdTargetState); // int
+}
+
+/// Addition of obstacle to dynamic navigation mesh.
+EVENT(E_NAVIGATION_OBSTACLE_ADDED, NavigationObstacleAdded)
+{
+    PARAM(P_NODE, Node); // Node pointer
+    PARAM(P_OBSTACLE, Obstacle); // Obstacle pointer
+    PARAM(P_POSITION, Position); // Vector3
+    PARAM(P_RADIUS, Radius); // float
+    PARAM(P_HEIGHT, Height); // float
+}
+
+/// Removal of obstacle from dynamic navigation mesh.
+EVENT(E_NAVIGATION_OBSTACLE_REMOVED, NavigationObstacleRemoved)
+{
+    PARAM(P_NODE, Node); // Node pointer
+    PARAM(P_OBSTACLE, Obstacle); // Obstacle pointer
+    PARAM(P_POSITION, Position); // Vector3
+    PARAM(P_RADIUS, Radius); // float
+    PARAM(P_HEIGHT, Height); // float
+}
+
+}

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

@@ -0,0 +1,117 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../Navigation/Obstacle.h"
+
+#include "../Core/Context.h"
+#include "../Graphics/DebugRenderer.h"
+#include "../Navigation/DynamicNavigationMesh.h"
+#include "../IO/Log.h"
+#include "../Navigation/NavigationEvents.h"
+#include "../Scene/Scene.h"
+
+#include "../DebugNew.h"
+
+namespace Urho3D
+{
+
+extern const char* NAVIGATION_CATEGORY;
+
+Obstacle::Obstacle(Context* context) :
+    Component(context),
+    height_(5.0f),
+    radius_(5.0f),
+    obstacleId_(0)
+{
+}
+
+Obstacle::~Obstacle()
+{
+    if (obstacleId_ > 0 && ownerMesh_)
+        ownerMesh_->RemoveObstacle(this);
+}
+
+void Obstacle::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Obstacle>(NAVIGATION_CATEGORY);
+    COPY_BASE_ATTRIBUTES(Component);
+    ACCESSOR_ATTRIBUTE("Radius", GetRadius, SetRadius, float, 5.0f, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Height", GetHeight, SetHeight, float, 5.0f, AM_DEFAULT);
+}
+
+void Obstacle::OnSetEnabled()
+{
+    if (ownerMesh_)
+    {
+        if (IsEnabledEffective())
+            ownerMesh_->AddObstacle(this);
+        else
+            ownerMesh_->RemoveObstacle(this);
+    }
+}
+
+void Obstacle::SetHeight(float newHeight)
+{
+    height_ = newHeight;
+    if (ownerMesh_)
+        ownerMesh_->ObstacleChanged(this);
+    MarkNetworkUpdate();
+}
+
+void Obstacle::SetRadius(float newRadius)
+{
+    radius_ = newRadius;
+    if (ownerMesh_)
+        ownerMesh_->ObstacleChanged(this);
+    MarkNetworkUpdate();
+}
+
+void Obstacle::OnNodeSet(Node* node)
+{
+    if (node)
+    {
+        if (GetScene() == node)
+        {
+            LOGWARNING(GetTypeName() + " should not be created to the root scene node");
+            return;
+        }
+        if (!ownerMesh_)
+            ownerMesh_ = GetScene()->GetComponent<DynamicNavigationMesh>();
+        if (ownerMesh_)
+            ownerMesh_->AddObstacle(this);
+    }
+}
+
+void Obstacle::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (debug && IsEnabledEffective())
+        debug->AddCylinder(node_->GetWorldPosition(), radius_, height_, Color(0.0f, 1.0f, 1.0f), depthTest);
+}
+
+void Obstacle::DrawDebugGeometry(bool depthTest)
+{
+    Scene* scene = GetScene();
+    if (scene)
+        DrawDebugGeometry(scene->GetComponent<DebugRenderer>(), depthTest);
+}
+
+}

+ 84 - 0
Source/Atomic/Navigation/Obstacle.h

@@ -0,0 +1,84 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Container/Ptr.h"
+#include "../Scene/Component.h"
+
+namespace Urho3D
+{
+
+class DynamicNavigationMesh;
+
+/// Obstacle for dynamic navigation mesh.
+class URHO3D_API Obstacle : public Component
+{
+    OBJECT(Obstacle)
+    friend class DynamicNavigationMesh;
+
+public:
+    /// Construct.
+    Obstacle(Context*);
+    /// Destruct.
+    virtual ~Obstacle();
+
+    /// Register Obstacle with engine context.
+    static void RegisterObject(Context*);
+
+    /// Update the owning mesh when enabled status has changed.
+    virtual void OnSetEnabled();
+
+    /// Get the height of this obstacle.
+    float GetHeight() const { return height_; }
+    /// Set the height of this obstacle.
+    void SetHeight(float);
+    /// Get the blocking radius of this obstacle.
+    float GetRadius() const { return radius_; }
+    /// Set the blocking radius of this obstacle.
+    void SetRadius(float);
+
+    /// Get the internal obstacle ID.
+    unsigned GetObstacleID() const { return obstacleId_; }
+
+    /// Render debug information.
+    virtual void DrawDebugGeometry(DebugRenderer*, bool depthTest);
+    /// Simplified rendering of debug information for script usage.
+    void DrawDebugGeometry(bool depthTest);
+
+protected:
+    /// Handle node being assigned, identify our DynamicNavigationMesh.
+    virtual void OnNodeSet(Node* node);
+
+private:
+    /// Radius of this obstacle.
+    float radius_;
+    /// Height of this obstacle, extends 1/2 height below and 1/2 height above the owning node's position.
+    float height_;
+
+    /// Id received from tile cache.
+    unsigned obstacleId_;
+    /// Pointer to the navigation mesh we belong to.
+    WeakPtr<DynamicNavigationMesh> ownerMesh_;
+};
+
+}

+ 36 - 0
Source/ThirdParty/DetourCrowd/CMakeLists.txt

@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2008-2015 the Urho3D project.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+# Define target name
+set (TARGET_NAME DetourCrowd)
+
+# Define source files
+define_source_files (GLOB_CPP_PATTERNS source/*.cpp GLOB_H_PATTERNS include/*.h)
+
+# Define dependency libs
+set (INCLUDE_DIRS include ../Detour/include)
+
+# Setup target
+setup_library ()
+
+# Install headers for building the Urho3D library
+install_header_files (DIRECTORY include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/DetourCrowd FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant

+ 18 - 0
Source/ThirdParty/DetourCrowd/License.txt

@@ -0,0 +1,18 @@
+Copyright (c) 2009 Mikko Mononen [email protected]
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+

+ 62 - 0
Source/ThirdParty/DetourCrowd/README.md

@@ -0,0 +1,62 @@
+
+Recast & Detour
+===============
+
+[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/memononen/recastnavigation/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
+
+![screenshot of a navmesh baked with the sample program](/RecastDemo/screenshot.png?raw=true)
+
+## Recast
+
+Recast is state of the art navigation mesh construction toolset for games.
+
+* It is automatic, which means that you can throw any level geometry at it and you will get robust mesh out
+* It is fast which means swift turnaround times for level designers
+* It is open source so it comes with full source and you can customize it to your heart's content. 
+
+The Recast process starts with constructing a voxel mold from a level geometry 
+and then casting a navigation mesh over it. The process consists of three steps, 
+building the voxel mold, partitioning the mold into simple regions, peeling off 
+the regions as simple polygons.
+
+1. The voxel mold is build from the input triangle mesh by rasterizing the triangles into a multi-layer heightfield. Some simple filters are  then applied to the mold to prune out locations where the character would not be able to move.
+2. The walkable areas described by the mold are divided into simple overlayed 2D regions. The resulting regions have only one non-overlapping contour, which simplifies the final step of the process tremendously.
+3. The navigation polygons are peeled off from the regions by first tracing the boundaries and then simplifying them. The resulting polygons are finally converted to convex polygons which makes them perfect for pathfinding and spatial reasoning about the level. 
+
+
+## Detour
+
+Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
+
+Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows you to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes. 
+
+
+## Recast Demo
+
+You can find a comprehensive demo project in RecastDemo folder. It is a kitchen sink demo containing all the functionality of the library. If you are new to Recast & Detour, check out [Sample_SoloMesh.cpp](/RecastDemo/Source/Sample_SoloMesh.cpp) to get started with building navmeshes and [NavMeshTesterTool.cpp](/RecastDemo/Source/NavMeshTesterTool.cpp) to see how Detour can be used to find paths.
+
+### Building RecastDemo
+
+RecastDemo uses [premake4](http://industriousone.com/premake) to build platform specific projects, now is good time to install it if you don't have it already. To build *RecasDemo*, in your favorite terminal navigate into the `RecastDemo` folder, then:
+
+- *OS X*: `premake4 xcode4`
+- *Windows*: `premake4 vs2010`
+- *Linux*: `premake4 gmake`
+
+See premake4 documentation for full list of supported build file types. The projects will be created in `RecastDemo/Build` folder. And after you have compiled the project, the *RecastDemo* executable will be located in `RecastDemo/Bin` folder.
+
+
+## Integrating with your own project
+
+It is recommended to add the source directories `DebugUtils`, `Detour`, `DetourCrowd`, `DetourTileCache`, and `Recast` into your own project depending on which parts of the project you need. For example your level building tool could include DebugUtils, Recast, and Detour, and your game runtime could just include Detour.
+
+
+## Discuss
+
+- Discuss Recast & Detour: http://groups.google.com/group/recastnavigation
+- Development blog: http://digestingduck.blogspot.com/
+
+
+## License
+
+Recast & Detour is licensed under ZLib license, see License.txt for more information.

+ 120 - 0
Source/ThirdParty/DetourCrowd/Readme.txt

@@ -0,0 +1,120 @@
+
+Recast & Detour Version 1.4
+
+
+Recast
+
+Recast is state of the art navigation mesh construction toolset for games.
+
+    * It is automatic, which means that you can throw any level geometry
+      at it and you will get robust mesh out
+    * It is fast which means swift turnaround times for level designers
+    * It is open source so it comes with full source and you can
+      customize it to your hearts content. 
+
+The Recast process starts with constructing a voxel mold from a level geometry 
+and then casting a navigation mesh over it. The process consists of three steps, 
+building the voxel mold, partitioning the mold into simple regions, peeling off 
+the regions as simple polygons.
+
+   1. The voxel mold is build from the input triangle mesh by rasterizing 
+      the triangles into a multi-layer heightfield. Some simple filters are 
+      then applied to the mold to prune out locations where the character 
+      would not be able to move.
+   2. The walkable areas described by the mold are divided into simple 
+      overlayed 2D regions. The resulting regions have only one non-overlapping 
+      contour, which simplifies the final step of the process tremendously.
+   3. The navigation polygons are peeled off from the regions by first tracing 
+      the boundaries and then simplifying them. The resulting polygons are 
+      finally converted to convex polygons which makes them perfect for 
+      pathfinding and spatial reasoning about the level. 
+
+The toolset code is located in the Recast folder and demo application using the Recast
+toolset is located in the RecastDemo folder.
+
+The project files with this distribution can be compiled with Microsoft Visual C++ 2008
+(you can download it for free) and XCode 3.1.
+
+
+Detour
+
+Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
+
+Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes. 
+
+
+Latest code available at http://code.google.com/p/recastnavigation/
+
+
+--
+
+Release Notes
+
+----------------
+* Recast 1.4
+  Released August 24th, 2009
+
+- Added detail height mesh generation (RecastDetailMesh.cpp) for single,
+  tiled statmeshes as well as tilemesh.
+- Added feature to contour tracing which detects extra vertices along
+  tile edges which should be removed later.
+- Changed the tiled stat mesh preprocess, so that it first generated
+  polymeshes per tile and finally combines them.
+- Fixed bug in the GUI code where invisible buttons could be pressed.
+
+----------------
+* Recast 1.31
+  Released July 24th, 2009
+
+- Better cost and heuristic functions.
+- Fixed tile navmesh raycast on tile borders.
+
+----------------
+* Recast 1.3
+  Released July 14th, 2009
+
+- Added dtTileNavMesh which allows to dynamically add and remove navmesh pieces at runtime.
+- Renamed stat navmesh types to dtStat* (i.e. dtPoly is now dtStatPoly).
+- Moved common code used by tile and stat navmesh to DetourNode.h/cpp and DetourCommon.h/cpp.
+- Refactores the demo code.
+
+----------------
+* Recast 1.2
+  Released June 17th, 2009
+
+- Added tiled mesh generation. The tiled generation allows to generate navigation for
+  much larger worlds, it removes some of the artifacts that comes from distance fields
+  in open areas, and allows later streaming and dynamic runtime generation
+- Improved and added some debug draw modes
+- API change: The helper function rcBuildNavMesh does not exists anymore,
+  had to change few internal things to cope with the tiled processing,
+  similar API functionality will be added later once the tiled process matures
+- The demo is getting way too complicated, need to split demos
+- Fixed several filtering functions so that the mesh is tighter to the geometry,
+  sometimes there could be up error up to tow voxel units close to walls,
+  now it should be just one.
+
+----------------
+* Recast 1.1
+  Released April 11th, 2009
+
+This is the first release of Detour.
+
+----------------
+* Recast 1.0
+  Released March 29th, 2009
+
+This is the first release of Recast.
+
+The process is not always as robust as I would wish. The watershed phase sometimes swallows tiny islands
+which are close to edges. These droppings are handled in rcBuildContours, but the code is not
+particularly robust either.
+
+Another non-robust case is when portal contours (contours shared between two regions) are always
+assumed to be straight. That can lead to overlapping contours specially when the level has
+large open areas.
+
+
+
+Mikko Mononen
[email protected]

+ 20 - 0
Source/ThirdParty/DetourCrowd/TODO.txt

@@ -0,0 +1,20 @@
+TODO/Roadmap
+
+Summer/Autumn 2009
+
+- Off mesh links (jump links)
+- Area annotations
+- Embed extra data per polygon
+- Height conforming navmesh
+
+
+Autumn/Winter 2009/2010
+
+- Detour path following
+- More dynamic example with tile navmesh
+- Faster small tile process
+
+
+More info at http://digestingduck.blogspot.com/2009/07/recast-and-detour-roadmap.html
+
+-

+ 450 - 0
Source/ThirdParty/DetourCrowd/include/DetourCrowd.h

@@ -0,0 +1,450 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURCROWD_H
+#define DETOURCROWD_H
+
+#include "DetourNavMeshQuery.h"
+#include "DetourObstacleAvoidance.h"
+#include "DetourLocalBoundary.h"
+#include "DetourPathCorridor.h"
+#include "DetourProximityGrid.h"
+#include "DetourPathQueue.h"
+
+/// The maximum number of neighbors that a crowd agent can take into account
+/// for steering decisions.
+/// @ingroup crowd
+static const int DT_CROWDAGENT_MAX_NEIGHBOURS = 6;
+
+/// The maximum number of corners a crowd agent will look ahead in the path.
+/// This value is used for sizing the crowd agent corner buffers.
+/// Due to the behavior of the crowd manager, the actual number of useful
+/// corners will be one less than this number.
+/// @ingroup crowd
+static const int DT_CROWDAGENT_MAX_CORNERS = 4;
+
+/// The maximum number of crowd avoidance configurations supported by the
+/// crowd manager.
+/// @ingroup crowd
+/// @see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(), dtCrowd::getObstacleAvoidanceParams(),
+///		 dtCrowdAgentParams::obstacleAvoidanceType
+static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
+
+/// The maximum number of query filter types supported by the crowd manager.
+/// @ingroup crowd
+/// @see dtQueryFilter, dtCrowd::getFilter() dtCrowd::getEditableFilter(),
+///		dtCrowdAgentParams::queryFilterType
+static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
+
+/// Provides neighbor data for agents managed by the crowd.
+/// @ingroup crowd
+/// @see dtCrowdAgent::neis, dtCrowd
+struct dtCrowdNeighbour
+{
+	int idx;		///< The index of the neighbor in the crowd.
+	float dist;		///< The distance between the current agent and the neighbor.
+};
+
+/// The type of navigation mesh polygon the agent is currently traversing.
+/// @ingroup crowd
+enum CrowdAgentState
+{
+	DT_CROWDAGENT_STATE_INVALID,		///< The agent is not in a valid state.
+	DT_CROWDAGENT_STATE_WALKING,		///< The agent is traversing a normal navigation mesh polygon.
+	DT_CROWDAGENT_STATE_OFFMESH,		///< The agent is traversing an off-mesh connection.
+};
+
+/// Configuration parameters for a crowd agent.
+/// @ingroup crowd
+struct dtCrowdAgentParams
+{
+	float radius;						///< Agent radius. [Limit: >= 0]
+	float height;						///< Agent height. [Limit: > 0]
+	float maxAcceleration;				///< Maximum allowed acceleration. [Limit: >= 0]
+	float maxSpeed;						///< Maximum allowed speed. [Limit: >= 0]
+
+	/// Defines how close a collision element must be before it is considered for steering behaviors. [Limits: > 0]
+	float collisionQueryRange;
+
+	float pathOptimizationRange;		///< The path visibility optimization range. [Limit: > 0]
+
+	/// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
+	float separationWeight;
+
+	/// Flags that impact steering behavior. (See: #UpdateFlags)
+	unsigned char updateFlags;
+
+	/// The index of the avoidance configuration to use for the agent. 
+	/// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
+	unsigned char obstacleAvoidanceType;	
+
+	/// The index of the query filter used by this agent.
+	unsigned char queryFilterType;
+
+	/// User defined data attached to the agent.
+	void* userData;
+};
+
+enum MoveRequestState
+{
+	DT_CROWDAGENT_TARGET_NONE = 0,
+	DT_CROWDAGENT_TARGET_FAILED,
+	DT_CROWDAGENT_TARGET_VALID,
+	DT_CROWDAGENT_TARGET_REQUESTING,
+	DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE,
+	DT_CROWDAGENT_TARGET_WAITING_FOR_PATH,
+	DT_CROWDAGENT_TARGET_VELOCITY,
+};
+
+/// Represents an agent managed by a #dtCrowd object.
+/// @ingroup crowd
+struct dtCrowdAgent
+{
+	/// True if the agent is active, false if the agent is in an unused slot in the agent pool.
+	bool active;
+
+	/// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState)
+	unsigned char state;
+
+	/// True if the agent has valid path (targetState == DT_CROWDAGENT_TARGET_VALID) and the path does not lead to the requested position, else false.
+	bool partial;
+
+	/// The path corridor the agent is using.
+	dtPathCorridor corridor;
+
+	/// The local boundary data for the agent.
+	dtLocalBoundary boundary;
+	
+	/// Time since the agent's path corridor was optimized.
+	float topologyOptTime;
+	
+	/// The known neighbors of the agent.
+	dtCrowdNeighbour neis[DT_CROWDAGENT_MAX_NEIGHBOURS];
+
+	/// The number of neighbors.
+	int nneis;
+	
+	/// The desired speed.
+	float desiredSpeed;
+
+	float npos[3];		///< The current agent position. [(x, y, z)]
+	float disp[3];
+	float dvel[3];		///< The desired velocity of the agent. [(x, y, z)]
+	float nvel[3];
+	float vel[3];		///< The actual velocity of the agent. [(x, y, z)]
+
+	/// The agent's configuration parameters.
+	dtCrowdAgentParams params;
+
+	/// The local path corridor corners for the agent. (Staight path.) [(x, y, z) * #ncorners]
+	float cornerVerts[DT_CROWDAGENT_MAX_CORNERS*3];
+
+	/// The local path corridor corner flags. (See: #dtStraightPathFlags) [(flags) * #ncorners]
+	unsigned char cornerFlags[DT_CROWDAGENT_MAX_CORNERS];
+
+	/// The reference id of the polygon being entered at the corner. [(polyRef) * #ncorners]
+	dtPolyRef cornerPolys[DT_CROWDAGENT_MAX_CORNERS];
+
+	/// The number of corners.
+	int ncorners;
+	
+	unsigned char targetState;			///< State of the movement request.
+	dtPolyRef targetRef;				///< Target polyref of the movement request.
+	float targetPos[3];					///< Target position of the movement request (or velocity in case of DT_CROWDAGENT_TARGET_VELOCITY).
+	dtPathQueueRef targetPathqRef;		///< Path finder ref.
+	bool targetReplan;					///< Flag indicating that the current path is being replanned.
+	float targetReplanTime;				/// <Time since the agent's target was replanned.
+};
+
+struct dtCrowdAgentAnimation
+{
+	bool active;
+	float initPos[3], startPos[3], endPos[3];
+	dtPolyRef polyRef;
+	float t, tmax;
+};
+
+/// Crowd agent update flags.
+/// @ingroup crowd
+/// @see dtCrowdAgentParams::updateFlags
+enum UpdateFlags
+{
+	DT_CROWD_ANTICIPATE_TURNS = 1,
+	DT_CROWD_OBSTACLE_AVOIDANCE = 2,
+	DT_CROWD_SEPARATION = 4,
+	DT_CROWD_OPTIMIZE_VIS = 8,			///< Use #dtPathCorridor::optimizePathVisibility() to optimize the agent path.
+	DT_CROWD_OPTIMIZE_TOPO = 16,		///< Use dtPathCorridor::optimizePathTopology() to optimize the agent path.
+};
+
+struct dtCrowdAgentDebugInfo
+{
+	int idx;
+	float optStart[3], optEnd[3];
+	dtObstacleAvoidanceDebugData* vod;
+};
+
+/// Provides local steering behaviors for a group of agents. 
+/// @ingroup crowd
+class dtCrowd
+{
+	int m_maxAgents;
+	dtCrowdAgent* m_agents;
+	dtCrowdAgent** m_activeAgents;
+	dtCrowdAgentAnimation* m_agentAnims;
+	
+	dtPathQueue m_pathq;
+
+	dtObstacleAvoidanceParams m_obstacleQueryParams[DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS];
+	dtObstacleAvoidanceQuery* m_obstacleQuery;
+	
+	dtProximityGrid* m_grid;
+	
+	dtPolyRef* m_pathResult;
+	int m_maxPathResult;
+	
+	float m_ext[3];
+
+	dtQueryFilter m_filters[DT_CROWD_MAX_QUERY_FILTER_TYPE];
+
+	float m_maxAgentRadius;
+
+	int m_velocitySampleCount;
+
+	dtNavMeshQuery* m_navquery;
+
+	void updateTopologyOptimization(dtCrowdAgent** agents, const int nagents, const float dt);
+	void updateMoveRequest(const float dt);
+	void checkPathValidity(dtCrowdAgent** agents, const int nagents, const float dt);
+
+	inline int getAgentIndex(const dtCrowdAgent* agent) const  { return (int)(agent - m_agents); }
+
+	bool requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos);
+
+	void purge();
+	
+public:
+	dtCrowd();
+	~dtCrowd();
+	
+	/// Initializes the crowd.  
+	///  @param[in]		maxAgents		The maximum number of agents the crowd can manage. [Limit: >= 1]
+	///  @param[in]		maxAgentRadius	The maximum radius of any agent that will be added to the crowd. [Limit: > 0]
+	///  @param[in]		nav				The navigation mesh to use for planning.
+	/// @return True if the initialization succeeded.
+	bool init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav);
+	
+	/// Sets the shared avoidance configuration for the specified index.
+	///  @param[in]		idx		The index. [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
+	///  @param[in]		params	The new configuration.
+	void setObstacleAvoidanceParams(const int idx, const dtObstacleAvoidanceParams* params);
+
+	/// Gets the shared avoidance configuration for the specified index.
+	///  @param[in]		idx		The index of the configuration to retreive. 
+	///							[Limits:  0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
+	/// @return The requested configuration.
+	const dtObstacleAvoidanceParams* getObstacleAvoidanceParams(const int idx) const;
+	
+	/// Gets the specified agent from the pool.
+	///	 @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
+	/// @return The requested agent.
+	const dtCrowdAgent* getAgent(const int idx);
+
+	/// Gets the specified agent from the pool.
+	///	 @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
+	/// @return The requested agent.
+	dtCrowdAgent* getEditableAgent(const int idx);
+
+	/// The maximum number of agents that can be managed by the object.
+	/// @return The maximum number of agents.
+	int getAgentCount() const;
+	
+	/// Adds a new agent to the crowd.
+	///  @param[in]		pos		The requested position of the agent. [(x, y, z)]
+	///  @param[in]		params	The configutation of the agent.
+	/// @return The index of the agent in the agent pool. Or -1 if the agent could not be added.
+	int addAgent(const float* pos, const dtCrowdAgentParams* params);
+
+	/// Updates the specified agent's configuration.
+	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
+	///  @param[in]		params	The new agent configuration.
+	void updateAgentParameters(const int idx, const dtCrowdAgentParams* params);
+
+	/// Removes the agent from the crowd.
+	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
+	void removeAgent(const int idx);
+	
+	/// Submits a new move request for the specified agent.
+	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
+	///  @param[in]		ref		The position's polygon reference.
+	///  @param[in]		pos		The position within the polygon. [(x, y, z)]
+	/// @return True if the request was successfully submitted.
+	bool requestMoveTarget(const int idx, dtPolyRef ref, const float* pos);
+
+	/// Submits a new move request for the specified agent.
+	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
+	///  @param[in]		vel		The movement velocity. [(x, y, z)]
+	/// @return True if the request was successfully submitted.
+	bool requestMoveVelocity(const int idx, const float* vel);
+
+	/// Resets any request for the specified agent.
+	///  @param[in]		idx		The agent index. [Limits: 0 <= value < #getAgentCount()]
+	/// @return True if the request was successfully reseted.
+	bool resetMoveTarget(const int idx);
+
+	/// Gets the active agents int the agent pool.
+	///  @param[out]	agents		An array of agent pointers. [(#dtCrowdAgent *) * maxAgents]
+	///  @param[in]		maxAgents	The size of the crowd agent array.
+	/// @return The number of agents returned in @p agents.
+	int getActiveAgents(dtCrowdAgent** agents, const int maxAgents);
+
+	/// Updates the steering and positions of all agents.
+	///  @param[in]		dt		The time, in seconds, to update the simulation. [Limit: > 0]
+	///  @param[out]	debug	A debug object to load with debug information. [Opt]
+	void update(const float dt, dtCrowdAgentDebugInfo* debug);
+	
+	/// Gets the filter used by the crowd.
+	/// @return The filter used by the crowd.
+	inline const dtQueryFilter* getFilter(const int i) const { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
+	
+	/// Gets the filter used by the crowd.
+	/// @return The filter used by the crowd.
+	inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
+
+	/// Gets the search extents [(x, y, z)] used by the crowd for query operations. 
+	/// @return The search extents used by the crowd. [(x, y, z)]
+	const float* getQueryExtents() const { return m_ext; }
+	
+	/// Gets the velocity sample count.
+	/// @return The velocity sample count.
+	inline int getVelocitySampleCount() const { return m_velocitySampleCount; }
+	
+	/// Gets the crowd's proximity grid.
+	/// @return The crowd's proximity grid.
+	const dtProximityGrid* getGrid() const { return m_grid; }
+
+	/// Gets the crowd's path request queue.
+	/// @return The crowd's path request queue.
+	const dtPathQueue* getPathQueue() const { return &m_pathq; }
+
+	/// Gets the query object used by the crowd.
+	const dtNavMeshQuery* getNavMeshQuery() const { return m_navquery; }
+};
+
+/// Allocates a crowd object using the Detour allocator.
+/// @return A crowd object that is ready for initialization, or null on failure.
+///  @ingroup crowd
+dtCrowd* dtAllocCrowd();
+
+/// Frees the specified crowd object using the Detour allocator.
+///  @param[in]		ptr		A crowd object allocated using #dtAllocCrowd
+///  @ingroup crowd
+void dtFreeCrowd(dtCrowd* ptr);
+
+
+#endif // DETOURCROWD_H
+
+///////////////////////////////////////////////////////////////////////////
+
+// This section contains detailed documentation for members that don't have
+// a source file. It reduces clutter in the main section of the header.
+
+/**
+
+@defgroup crowd Crowd
+
+Members in this module implement local steering and dynamic avoidance features.
+
+The crowd is the big beast of the navigation features. It not only handles a 
+lot of the path management for you, but also local steering and dynamic 
+avoidance between members of the crowd. I.e. It can keep your agents from 
+running into each other.
+
+Main class: #dtCrowd
+
+The #dtNavMeshQuery and #dtPathCorridor classes provide perfectly good, easy 
+to use path planning features. But in the end they only give you points that 
+your navigation client should be moving toward. When it comes to deciding things 
+like agent velocity and steering to avoid other agents, that is up to you to 
+implement. Unless, of course, you decide to use #dtCrowd.
+
+Basically, you add an agent to the crowd, providing various configuration 
+settings such as maximum speed and acceleration. You also provide a local 
+target to more toward. The crowd manager then provides, with every update, the 
+new agent position and velocity for the frame. The movement will be 
+constrained to the navigation mesh, and steering will be applied to ensure 
+agents managed by the crowd do not collide with each other.
+
+This is very powerful feature set. But it comes with limitations.
+
+The biggest limitation is that you must give control of the agent's position 
+completely over to the crowd manager. You can update things like maximum speed 
+and acceleration. But in order for the crowd manager to do its thing, it can't 
+allow you to constantly be giving it overrides to position and velocity. So 
+you give up direct control of the agent's movement. It belongs to the crowd.
+
+The second biggest limitation revolves around the fact that the crowd manager 
+deals with local planning. So the agent's target should never be more than 
+256 polygons aways from its current position. If it is, you risk 
+your agent failing to reach its target. So you may still need to do long 
+distance planning and provide the crowd manager with intermediate targets.
+
+Other significant limitations:
+
+- All agents using the crowd manager will use the same #dtQueryFilter.
+- Crowd management is relatively expensive. The maximum agents under crowd 
+  management at any one time is between 20 and 30.  A good place to start
+  is a maximum of 25 agents for 0.5ms per frame.
+
+@note This is a summary list of members.  Use the index or search 
+feature to find minor members.
+
+@struct dtCrowdAgentParams
+@see dtCrowdAgent, dtCrowd::addAgent(), dtCrowd::updateAgentParameters()
+
+@var dtCrowdAgentParams::obstacleAvoidanceType
+@par
+
+#dtCrowd permits agents to use different avoidance configurations.  This value 
+is the index of the #dtObstacleAvoidanceParams within the crowd.
+
+@see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(), 
+	 dtCrowd::getObstacleAvoidanceParams()
+
+@var dtCrowdAgentParams::collisionQueryRange
+@par
+
+Collision elements include other agents and navigation mesh boundaries.
+
+This value is often based on the agent radius and/or maximum speed. E.g. radius * 8
+
+@var dtCrowdAgentParams::pathOptimizationRange
+@par
+
+Only applicalbe if #updateFlags includes the #DT_CROWD_OPTIMIZE_VIS flag.
+
+This value is often based on the agent radius. E.g. radius * 30
+
+@see dtPathCorridor::optimizePathVisibility()
+
+@var dtCrowdAgentParams::separationWeight
+@par
+
+A higher value will result in agents trying to stay farther away from each other at 
+the cost of more difficult steering in tight spaces.
+
+*/

+ 61 - 0
Source/ThirdParty/DetourCrowd/include/DetourLocalBoundary.h

@@ -0,0 +1,61 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURLOCALBOUNDARY_H
+#define DETOURLOCALBOUNDARY_H
+
+#include "DetourNavMeshQuery.h"
+
+
+class dtLocalBoundary
+{
+	static const int MAX_LOCAL_SEGS = 8;
+	static const int MAX_LOCAL_POLYS = 16;
+	
+	struct Segment
+	{
+		float s[6];	///< Segment start/end
+		float d;	///< Distance for pruning.
+	};
+	
+	float m_center[3];
+	Segment m_segs[MAX_LOCAL_SEGS];
+	int m_nsegs;
+	
+	dtPolyRef m_polys[MAX_LOCAL_POLYS];
+	int m_npolys;
+
+	void addSegment(const float dist, const float* seg);
+	
+public:
+	dtLocalBoundary();
+	~dtLocalBoundary();
+	
+	void reset();
+	
+	void update(dtPolyRef ref, const float* pos, const float collisionQueryRange,
+				dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+	
+	bool isValid(dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+	
+	inline const float* getCenter() const { return m_center; }
+	inline int getSegmentCount() const { return m_nsegs; }
+	inline const float* getSegment(int i) const { return m_segs[i].s; }
+};
+
+#endif // DETOURLOCALBOUNDARY_H

+ 154 - 0
Source/ThirdParty/DetourCrowd/include/DetourObstacleAvoidance.h

@@ -0,0 +1,154 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOUROBSTACLEAVOIDANCE_H
+#define DETOUROBSTACLEAVOIDANCE_H
+
+struct dtObstacleCircle
+{
+	float p[3];				///< Position of the obstacle
+	float vel[3];			///< Velocity of the obstacle
+	float dvel[3];			///< Velocity of the obstacle
+	float rad;				///< Radius of the obstacle
+	float dp[3], np[3];		///< Use for side selection during sampling.
+};
+
+struct dtObstacleSegment
+{
+	float p[3], q[3];		///< End points of the obstacle segment
+	bool touch;
+};
+
+
+class dtObstacleAvoidanceDebugData
+{
+public:
+	dtObstacleAvoidanceDebugData();
+	~dtObstacleAvoidanceDebugData();
+	
+	bool init(const int maxSamples);
+	void reset();
+	void addSample(const float* vel, const float ssize, const float pen,
+				   const float vpen, const float vcpen, const float spen, const float tpen);
+	
+	void normalizeSamples();
+	
+	inline int getSampleCount() const { return m_nsamples; }
+	inline const float* getSampleVelocity(const int i) const { return &m_vel[i*3]; }
+	inline float getSampleSize(const int i) const { return m_ssize[i]; }
+	inline float getSamplePenalty(const int i) const { return m_pen[i]; }
+	inline float getSampleDesiredVelocityPenalty(const int i) const { return m_vpen[i]; }
+	inline float getSampleCurrentVelocityPenalty(const int i) const { return m_vcpen[i]; }
+	inline float getSamplePreferredSidePenalty(const int i) const { return m_spen[i]; }
+	inline float getSampleCollisionTimePenalty(const int i) const { return m_tpen[i]; }
+
+private:
+	int m_nsamples;
+	int m_maxSamples;
+	float* m_vel;
+	float* m_ssize;
+	float* m_pen;
+	float* m_vpen;
+	float* m_vcpen;
+	float* m_spen;
+	float* m_tpen;
+};
+
+dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData();
+void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr);
+
+
+static const int DT_MAX_PATTERN_DIVS = 32;	///< Max numver of adaptive divs.
+static const int DT_MAX_PATTERN_RINGS = 4;	///< Max number of adaptive rings.
+
+struct dtObstacleAvoidanceParams
+{
+	float velBias;
+	float weightDesVel;
+	float weightCurVel;
+	float weightSide;
+	float weightToi;
+	float horizTime;
+	unsigned char gridSize;	///< grid
+	unsigned char adaptiveDivs;	///< adaptive
+	unsigned char adaptiveRings;	///< adaptive
+	unsigned char adaptiveDepth;	///< adaptive
+};
+
+class dtObstacleAvoidanceQuery
+{
+public:
+	dtObstacleAvoidanceQuery();
+	~dtObstacleAvoidanceQuery();
+	
+	bool init(const int maxCircles, const int maxSegments);
+	
+	void reset();
+
+	void addCircle(const float* pos, const float rad,
+				   const float* vel, const float* dvel);
+				   
+	void addSegment(const float* p, const float* q);
+
+	int sampleVelocityGrid(const float* pos, const float rad, const float vmax,
+						   const float* vel, const float* dvel, float* nvel,
+						   const dtObstacleAvoidanceParams* params,
+						   dtObstacleAvoidanceDebugData* debug = 0);
+
+	int sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
+							   const float* vel, const float* dvel, float* nvel,
+							   const dtObstacleAvoidanceParams* params, 
+							   dtObstacleAvoidanceDebugData* debug = 0);
+	
+	inline int getObstacleCircleCount() const { return m_ncircles; }
+	const dtObstacleCircle* getObstacleCircle(const int i) { return &m_circles[i]; }
+
+	inline int getObstacleSegmentCount() const { return m_nsegments; }
+	const dtObstacleSegment* getObstacleSegment(const int i) { return &m_segments[i]; }
+
+private:
+
+	void prepare(const float* pos, const float* dvel);
+
+	float processSample(const float* vcand, const float cs,
+						const float* pos, const float rad,
+						const float* vel, const float* dvel,
+						dtObstacleAvoidanceDebugData* debug);
+
+	dtObstacleCircle* insertCircle(const float dist);
+	dtObstacleSegment* insertSegment(const float dist);
+
+	dtObstacleAvoidanceParams m_params;
+	float m_invHorizTime;
+	float m_vmax;
+	float m_invVmax;
+
+	int m_maxCircles;
+	dtObstacleCircle* m_circles;
+	int m_ncircles;
+
+	int m_maxSegments;
+	dtObstacleSegment* m_segments;
+	int m_nsegments;
+};
+
+dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery();
+void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr);
+
+
+#endif // DETOUROBSTACLEAVOIDANCE_H

+ 146 - 0
Source/ThirdParty/DetourCrowd/include/DetourPathCorridor.h

@@ -0,0 +1,146 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOUTPATHCORRIDOR_H
+#define DETOUTPATHCORRIDOR_H
+
+#include "DetourNavMeshQuery.h"
+
+/// Represents a dynamic polygon corridor used to plan agent movement.
+/// @ingroup crowd, detour
+class dtPathCorridor
+{
+	float m_pos[3];
+	float m_target[3];
+	
+	dtPolyRef* m_path;
+	int m_npath;
+	int m_maxPath;
+	
+public:
+	dtPathCorridor();
+	~dtPathCorridor();
+	
+	/// Allocates the corridor's path buffer. 
+	///  @param[in]		maxPath		The maximum path size the corridor can handle.
+	/// @return True if the initialization succeeded.
+	bool init(const int maxPath);
+	
+	/// Resets the path corridor to the specified position.
+	///  @param[in]		ref		The polygon reference containing the position.
+	///  @param[in]		pos		The new position in the corridor. [(x, y, z)]
+	void reset(dtPolyRef ref, const float* pos);
+	
+	/// Finds the corners in the corridor from the position toward the target. (The straightened path.)
+	///  @param[out]	cornerVerts		The corner vertices. [(x, y, z) * cornerCount] [Size: <= maxCorners]
+	///  @param[out]	cornerFlags		The flag for each corner. [(flag) * cornerCount] [Size: <= maxCorners]
+	///  @param[out]	cornerPolys		The polygon reference for each corner. [(polyRef) * cornerCount] 
+	///  								[Size: <= @p maxCorners]
+	///  @param[in]		maxCorners		The maximum number of corners the buffers can hold.
+	///  @param[in]		navquery		The query object used to build the corridor.
+	///  @param[in]		filter			The filter to apply to the operation.
+	/// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners]
+	int findCorners(float* cornerVerts, unsigned char* cornerFlags,
+					dtPolyRef* cornerPolys, const int maxCorners,
+					dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+	
+	/// Attempts to optimize the path if the specified point is visible from the current position.
+	///  @param[in]		next					The point to search toward. [(x, y, z])
+	///  @param[in]		pathOptimizationRange	The maximum range to search. [Limit: > 0]
+	///  @param[in]		navquery				The query object used to build the corridor.
+	///  @param[in]		filter					The filter to apply to the operation.			
+	void optimizePathVisibility(const float* next, const float pathOptimizationRange,
+								dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+	
+	/// Attempts to optimize the path using a local area search. (Partial replanning.) 
+	///  @param[in]		navquery	The query object used to build the corridor.
+	///  @param[in]		filter		The filter to apply to the operation.	
+	bool optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+	
+	bool moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs,
+								   float* startPos, float* endPos,
+								   dtNavMeshQuery* navquery);
+
+	bool fixPathStart(dtPolyRef safeRef, const float* safePos);
+
+	bool trimInvalidPath(dtPolyRef safeRef, const float* safePos,
+						 dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+	
+	/// Checks the current corridor path to see if its polygon references remain valid. 
+	///  @param[in]		maxLookAhead	The number of polygons from the beginning of the corridor to search.
+	///  @param[in]		navquery		The query object used to build the corridor.
+	///  @param[in]		filter			The filter to apply to the operation.	
+	bool isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+	
+	/// Moves the position from the current location to the desired location, adjusting the corridor 
+	/// as needed to reflect the change.
+	///  @param[in]		npos		The desired new position. [(x, y, z)]
+	///  @param[in]		navquery	The query object used to build the corridor.
+	///  @param[in]		filter		The filter to apply to the operation.
+	/// @return Returns true if move succeeded.
+	bool movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+
+	/// Moves the target from the curent location to the desired location, adjusting the corridor
+	/// as needed to reflect the change. 
+	///  @param[in]		npos		The desired new target position. [(x, y, z)]
+	///  @param[in]		navquery	The query object used to build the corridor.
+	///  @param[in]		filter		The filter to apply to the operation.
+	/// @return Returns true if move succeeded.
+	bool moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
+	
+	/// Loads a new path and target into the corridor.
+	///  @param[in]		target		The target location within the last polygon of the path. [(x, y, z)]
+	///  @param[in]		path		The path corridor. [(polyRef) * @p npolys]
+	///  @param[in]		npath		The number of polygons in the path.
+	void setCorridor(const float* target, const dtPolyRef* polys, const int npath);
+	
+	/// Gets the current position within the corridor. (In the first polygon.)
+	/// @return The current position within the corridor.
+	inline const float* getPos() const { return m_pos; }
+
+	/// Gets the current target within the corridor. (In the last polygon.)
+	/// @return The current target within the corridor.
+	inline const float* getTarget() const { return m_target; }
+	
+	/// The polygon reference id of the first polygon in the corridor, the polygon containing the position.
+	/// @return The polygon reference id of the first polygon in the corridor. (Or zero if there is no path.)
+	inline dtPolyRef getFirstPoly() const { return m_npath ? m_path[0] : 0; }
+
+	/// The polygon reference id of the last polygon in the corridor, the polygon containing the target.
+	/// @return The polygon reference id of the last polygon in the corridor. (Or zero if there is no path.)
+	inline dtPolyRef getLastPoly() const { return m_npath ? m_path[m_npath-1] : 0; }
+	
+	/// The corridor's path.
+	/// @return The corridor's path. [(polyRef) * #getPathCount()]
+	inline const dtPolyRef* getPath() const { return m_path; }
+
+	/// The number of polygons in the current corridor path.
+	/// @return The number of polygons in the current corridor path.
+	inline int getPathCount() const { return m_npath; } 	
+};
+
+int dtMergeCorridorStartMoved(dtPolyRef* path, const int npath, const int maxPath,
+							  const dtPolyRef* visited, const int nvisited);
+
+int dtMergeCorridorEndMoved(dtPolyRef* path, const int npath, const int maxPath,
+							const dtPolyRef* visited, const int nvisited);
+
+int dtMergeCorridorStartShortcut(dtPolyRef* path, const int npath, const int maxPath,
+								 const dtPolyRef* visited, const int nvisited);
+
+#endif // DETOUTPATHCORRIDOR_H

+ 75 - 0
Source/ThirdParty/DetourCrowd/include/DetourPathQueue.h

@@ -0,0 +1,75 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURPATHQUEUE_H
+#define DETOURPATHQUEUE_H
+
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+static const unsigned int DT_PATHQ_INVALID = 0;
+
+typedef unsigned int dtPathQueueRef;
+
+class dtPathQueue
+{
+	struct PathQuery
+	{
+		dtPathQueueRef ref;
+		/// Path find start and end location.
+		float startPos[3], endPos[3];
+		dtPolyRef startRef, endRef;
+		/// Result.
+		dtPolyRef* path;
+		int npath;
+		/// State.
+		dtStatus status;
+		int keepAlive;
+		const dtQueryFilter* filter; ///< TODO: This is potentially dangerous!
+	};
+	
+	static const int MAX_QUEUE = 8;
+	PathQuery m_queue[MAX_QUEUE];
+	dtPathQueueRef m_nextHandle;
+	int m_maxPathSize;
+	int m_queueHead;
+	dtNavMeshQuery* m_navquery;
+	
+	void purge();
+	
+public:
+	dtPathQueue();
+	~dtPathQueue();
+	
+	bool init(const int maxPathSize, const int maxSearchNodeCount, dtNavMesh* nav);
+	
+	void update(const int maxIters);
+	
+	dtPathQueueRef request(dtPolyRef startRef, dtPolyRef endRef,
+						   const float* startPos, const float* endPos, 
+						   const dtQueryFilter* filter);
+	
+	dtStatus getRequestStatus(dtPathQueueRef ref) const;
+	
+	dtStatus getPathResult(dtPathQueueRef ref, dtPolyRef* path, int* pathSize, const int maxPath);
+	
+	inline const dtNavMeshQuery* getNavQuery() const { return m_navquery; }
+
+};
+
+#endif // DETOURPATHQUEUE_H

+ 70 - 0
Source/ThirdParty/DetourCrowd/include/DetourProximityGrid.h

@@ -0,0 +1,70 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURPROXIMITYGRID_H
+#define DETOURPROXIMITYGRID_H
+
+class dtProximityGrid
+{
+	int m_maxItems;
+	float m_cellSize;
+	float m_invCellSize;
+	
+	struct Item
+	{
+		unsigned short id;
+		short x,y;
+		unsigned short next;
+	};
+	Item* m_pool;
+	int m_poolHead;
+	int m_poolSize;
+	
+	unsigned short* m_buckets;
+	int m_bucketsSize;
+	
+	int m_bounds[4];
+	
+public:
+	dtProximityGrid();
+	~dtProximityGrid();
+	
+	bool init(const int maxItems, const float cellSize);
+	
+	void clear();
+	
+	void addItem(const unsigned short id,
+				 const float minx, const float miny,
+				 const float maxx, const float maxy);
+	
+	int queryItems(const float minx, const float miny,
+				   const float maxx, const float maxy,
+				   unsigned short* ids, const int maxIds) const;
+	
+	int getItemCountAt(const int x, const int y) const;
+	
+	inline const int* getBounds() const { return m_bounds; }
+	inline float getCellSize() const { return m_cellSize; }
+};
+
+dtProximityGrid* dtAllocProximityGrid();
+void dtFreeProximityGrid(dtProximityGrid* ptr);
+
+
+#endif // DETOURPROXIMITYGRID_H
+

+ 1446 - 0
Source/ThirdParty/DetourCrowd/source/DetourCrowd.cpp

@@ -0,0 +1,1446 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <string.h>
+#include <float.h>
+#include <stdlib.h>
+#include <new>
+#include "DetourCrowd.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+#include "DetourObstacleAvoidance.h"
+#include "DetourCommon.h"
+#include "DetourMath.h"
+#include "DetourAssert.h"
+#include "DetourAlloc.h"
+
+
+dtCrowd* dtAllocCrowd()
+{
+	void* mem = dtAlloc(sizeof(dtCrowd), DT_ALLOC_PERM);
+	if (!mem) return 0;
+	return new(mem) dtCrowd;
+}
+
+void dtFreeCrowd(dtCrowd* ptr)
+{
+	if (!ptr) return;
+	ptr->~dtCrowd();
+	dtFree(ptr);
+}
+
+
+static const int MAX_ITERS_PER_UPDATE = 100;
+
+static const int MAX_PATHQUEUE_NODES = 4096;
+static const int MAX_COMMON_NODES = 512;
+
+inline float tween(const float t, const float t0, const float t1)
+{
+	return dtClamp((t-t0) / (t1-t0), 0.0f, 1.0f);
+}
+
+static void integrate(dtCrowdAgent* ag, const float dt)
+{
+	// Fake dynamic constraint.
+	const float maxDelta = ag->params.maxAcceleration * dt;
+	float dv[3];
+	dtVsub(dv, ag->nvel, ag->vel);
+	float ds = dtVlen(dv);
+	if (ds > maxDelta)
+		dtVscale(dv, dv, maxDelta/ds);
+	dtVadd(ag->vel, ag->vel, dv);
+	
+	// Integrate
+	if (dtVlen(ag->vel) > 0.0001f)
+		dtVmad(ag->npos, ag->npos, ag->vel, dt);
+	else
+		dtVset(ag->vel,0,0,0);
+}
+
+static bool overOffmeshConnection(const dtCrowdAgent* ag, const float radius)
+{
+	if (!ag->ncorners)
+		return false;
+	
+	const bool offMeshConnection = (ag->cornerFlags[ag->ncorners-1] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ? true : false;
+	if (offMeshConnection)
+	{
+		const float distSq = dtVdist2DSqr(ag->npos, &ag->cornerVerts[(ag->ncorners-1)*3]);
+		if (distSq < radius*radius)
+			return true;
+	}
+	
+	return false;
+}
+
+static float getDistanceToGoal(const dtCrowdAgent* ag, const float range)
+{
+	if (!ag->ncorners)
+		return range;
+	
+	const bool endOfPath = (ag->cornerFlags[ag->ncorners-1] & DT_STRAIGHTPATH_END) ? true : false;
+	if (endOfPath)
+		return dtMin(dtVdist2D(ag->npos, &ag->cornerVerts[(ag->ncorners-1)*3]), range);
+	
+	return range;
+}
+
+static void calcSmoothSteerDirection(const dtCrowdAgent* ag, float* dir)
+{
+	if (!ag->ncorners)
+	{
+		dtVset(dir, 0,0,0);
+		return;
+	}
+	
+	const int ip0 = 0;
+	const int ip1 = dtMin(1, ag->ncorners-1);
+	const float* p0 = &ag->cornerVerts[ip0*3];
+	const float* p1 = &ag->cornerVerts[ip1*3];
+	
+	float dir0[3], dir1[3];
+	dtVsub(dir0, p0, ag->npos);
+	dtVsub(dir1, p1, ag->npos);
+	dir0[1] = 0;
+	dir1[1] = 0;
+	
+	float len0 = dtVlen(dir0);
+	float len1 = dtVlen(dir1);
+	if (len1 > 0.001f)
+		dtVscale(dir1,dir1,1.0f/len1);
+	
+	dir[0] = dir0[0] - dir1[0]*len0*0.5f;
+	dir[1] = 0;
+	dir[2] = dir0[2] - dir1[2]*len0*0.5f;
+	
+	dtVnormalize(dir);
+}
+
+static void calcStraightSteerDirection(const dtCrowdAgent* ag, float* dir)
+{
+	if (!ag->ncorners)
+	{
+		dtVset(dir, 0,0,0);
+		return;
+	}
+	dtVsub(dir, &ag->cornerVerts[0], ag->npos);
+	dir[1] = 0;
+	dtVnormalize(dir);
+}
+
+static int addNeighbour(const int idx, const float dist,
+						dtCrowdNeighbour* neis, const int nneis, const int maxNeis)
+{
+	// Insert neighbour based on the distance.
+	dtCrowdNeighbour* nei = 0;
+	if (!nneis)
+	{
+		nei = &neis[nneis];
+	}
+	else if (dist >= neis[nneis-1].dist)
+	{
+		if (nneis >= maxNeis)
+			return nneis;
+		nei = &neis[nneis];
+	}
+	else
+	{
+		int i;
+		for (i = 0; i < nneis; ++i)
+			if (dist <= neis[i].dist)
+				break;
+		
+		const int tgt = i+1;
+		const int n = dtMin(nneis-i, maxNeis-tgt);
+		
+		dtAssert(tgt+n <= maxNeis);
+		
+		if (n > 0)
+			memmove(&neis[tgt], &neis[i], sizeof(dtCrowdNeighbour)*n);
+		nei = &neis[i];
+	}
+	
+	memset(nei, 0, sizeof(dtCrowdNeighbour));
+	
+	nei->idx = idx;
+	nei->dist = dist;
+	
+	return dtMin(nneis+1, maxNeis);
+}
+
+static int getNeighbours(const float* pos, const float height, const float range,
+						 const dtCrowdAgent* skip, dtCrowdNeighbour* result, const int maxResult,
+						 dtCrowdAgent** agents, const int /*nagents*/, dtProximityGrid* grid)
+{
+	int n = 0;
+	
+	static const int MAX_NEIS = 32;
+	unsigned short ids[MAX_NEIS];
+	int nids = grid->queryItems(pos[0]-range, pos[2]-range,
+								pos[0]+range, pos[2]+range,
+								ids, MAX_NEIS);
+	
+	for (int i = 0; i < nids; ++i)
+	{
+		const dtCrowdAgent* ag = agents[ids[i]];
+		
+		if (ag == skip) continue;
+		
+		// Check for overlap.
+		float diff[3];
+		dtVsub(diff, pos, ag->npos);
+		if (dtMathFabs(diff[1]) >= (height+ag->params.height)/2.0f)
+			continue;
+		diff[1] = 0;
+		const float distSqr = dtVlenSqr(diff);
+		if (distSqr > dtSqr(range))
+			continue;
+		
+		n = addNeighbour(ids[i], distSqr, result, n, maxResult);
+	}
+	return n;
+}
+
+static int addToOptQueue(dtCrowdAgent* newag, dtCrowdAgent** agents, const int nagents, const int maxAgents)
+{
+	// Insert neighbour based on greatest time.
+	int slot = 0;
+	if (!nagents)
+	{
+		slot = nagents;
+	}
+	else if (newag->topologyOptTime <= agents[nagents-1]->topologyOptTime)
+	{
+		if (nagents >= maxAgents)
+			return nagents;
+		slot = nagents;
+	}
+	else
+	{
+		int i;
+		for (i = 0; i < nagents; ++i)
+			if (newag->topologyOptTime >= agents[i]->topologyOptTime)
+				break;
+		
+		const int tgt = i+1;
+		const int n = dtMin(nagents-i, maxAgents-tgt);
+		
+		dtAssert(tgt+n <= maxAgents);
+		
+		if (n > 0)
+			memmove(&agents[tgt], &agents[i], sizeof(dtCrowdAgent*)*n);
+		slot = i;
+	}
+	
+	agents[slot] = newag;
+	
+	return dtMin(nagents+1, maxAgents);
+}
+
+static int addToPathQueue(dtCrowdAgent* newag, dtCrowdAgent** agents, const int nagents, const int maxAgents)
+{
+	// Insert neighbour based on greatest time.
+	int slot = 0;
+	if (!nagents)
+	{
+		slot = nagents;
+	}
+	else if (newag->targetReplanTime <= agents[nagents-1]->targetReplanTime)
+	{
+		if (nagents >= maxAgents)
+			return nagents;
+		slot = nagents;
+	}
+	else
+	{
+		int i;
+		for (i = 0; i < nagents; ++i)
+			if (newag->targetReplanTime >= agents[i]->targetReplanTime)
+				break;
+		
+		const int tgt = i+1;
+		const int n = dtMin(nagents-i, maxAgents-tgt);
+		
+		dtAssert(tgt+n <= maxAgents);
+		
+		if (n > 0)
+			memmove(&agents[tgt], &agents[i], sizeof(dtCrowdAgent*)*n);
+		slot = i;
+	}
+	
+	agents[slot] = newag;
+	
+	return dtMin(nagents+1, maxAgents);
+}
+
+
+/**
+@class dtCrowd
+@par
+
+This is the core class of the @ref crowd module.  See the @ref crowd documentation for a summary
+of the crowd features.
+
+A common method for setting up the crowd is as follows:
+
+-# Allocate the crowd using #dtAllocCrowd.
+-# Initialize the crowd using #init().
+-# Set the avoidance configurations using #setObstacleAvoidanceParams().
+-# Add agents using #addAgent() and make an initial movement request using #requestMoveTarget().
+
+A common process for managing the crowd is as follows:
+
+-# Call #update() to allow the crowd to manage its agents.
+-# Retrieve agent information using #getActiveAgents().
+-# Make movement requests using #requestMoveTarget() when movement goal changes.
+-# Repeat every frame.
+
+Some agent configuration settings can be updated using #updateAgentParameters().  But the crowd owns the
+agent position.  So it is not possible to update an active agent's position.  If agent position
+must be fed back into the crowd, the agent must be removed and re-added.
+
+Notes: 
+
+- Path related information is available for newly added agents only after an #update() has been
+  performed.
+- Agent objects are kept in a pool and re-used.  So it is important when using agent objects to check the value of
+  #dtCrowdAgent::active to determine if the agent is actually in use or not.
+- This class is meant to provide 'local' movement. There is a limit of 256 polygons in the path corridor.  
+  So it is not meant to provide automatic pathfinding services over long distances.
+
+@see dtAllocCrowd(), dtFreeCrowd(), init(), dtCrowdAgent
+
+*/
+
+dtCrowd::dtCrowd() :
+	m_maxAgents(0),
+	m_agents(0),
+	m_activeAgents(0),
+	m_agentAnims(0),
+	m_obstacleQuery(0),
+	m_grid(0),
+	m_pathResult(0),
+	m_maxPathResult(0),
+	m_maxAgentRadius(0),
+	m_velocitySampleCount(0),
+	m_navquery(0)
+{
+}
+
+dtCrowd::~dtCrowd()
+{
+	purge();
+}
+
+void dtCrowd::purge()
+{
+	for (int i = 0; i < m_maxAgents; ++i)
+		m_agents[i].~dtCrowdAgent();
+	dtFree(m_agents);
+	m_agents = 0;
+	m_maxAgents = 0;
+	
+	dtFree(m_activeAgents);
+	m_activeAgents = 0;
+
+	dtFree(m_agentAnims);
+	m_agentAnims = 0;
+	
+	dtFree(m_pathResult);
+	m_pathResult = 0;
+	
+	dtFreeProximityGrid(m_grid);
+	m_grid = 0;
+
+	dtFreeObstacleAvoidanceQuery(m_obstacleQuery);
+	m_obstacleQuery = 0;
+	
+	dtFreeNavMeshQuery(m_navquery);
+	m_navquery = 0;
+}
+
+/// @par
+///
+/// May be called more than once to purge and re-initialize the crowd.
+bool dtCrowd::init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav)
+{
+	purge();
+	
+	m_maxAgents = maxAgents;
+	m_maxAgentRadius = maxAgentRadius;
+
+	dtVset(m_ext, m_maxAgentRadius*2.0f,m_maxAgentRadius*1.5f,m_maxAgentRadius*2.0f);
+	
+	m_grid = dtAllocProximityGrid();
+	if (!m_grid)
+		return false;
+	if (!m_grid->init(m_maxAgents*4, maxAgentRadius*3))
+		return false;
+	
+	m_obstacleQuery = dtAllocObstacleAvoidanceQuery();
+	if (!m_obstacleQuery)
+		return false;
+	if (!m_obstacleQuery->init(6, 8))
+		return false;
+
+	// Init obstacle query params.
+	memset(m_obstacleQueryParams, 0, sizeof(m_obstacleQueryParams));
+	for (int i = 0; i < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS; ++i)
+	{
+		dtObstacleAvoidanceParams* params = &m_obstacleQueryParams[i];
+		params->velBias = 0.4f;
+		params->weightDesVel = 2.0f;
+		params->weightCurVel = 0.75f;
+		params->weightSide = 0.75f;
+		params->weightToi = 2.5f;
+		params->horizTime = 2.5f;
+		params->gridSize = 33;
+		params->adaptiveDivs = 7;
+		params->adaptiveRings = 2;
+		params->adaptiveDepth = 5;
+	}
+	
+	// Allocate temp buffer for merging paths.
+	m_maxPathResult = 256;
+	m_pathResult = (dtPolyRef*)dtAlloc(sizeof(dtPolyRef)*m_maxPathResult, DT_ALLOC_PERM);
+	if (!m_pathResult)
+		return false;
+	
+	if (!m_pathq.init(m_maxPathResult, MAX_PATHQUEUE_NODES, nav))
+		return false;
+	
+	m_agents = (dtCrowdAgent*)dtAlloc(sizeof(dtCrowdAgent)*m_maxAgents, DT_ALLOC_PERM);
+	if (!m_agents)
+		return false;
+	
+	m_activeAgents = (dtCrowdAgent**)dtAlloc(sizeof(dtCrowdAgent*)*m_maxAgents, DT_ALLOC_PERM);
+	if (!m_activeAgents)
+		return false;
+
+	m_agentAnims = (dtCrowdAgentAnimation*)dtAlloc(sizeof(dtCrowdAgentAnimation)*m_maxAgents, DT_ALLOC_PERM);
+	if (!m_agentAnims)
+		return false;
+	
+	for (int i = 0; i < m_maxAgents; ++i)
+	{
+		new(&m_agents[i]) dtCrowdAgent();
+		m_agents[i].active = false;
+		if (!m_agents[i].corridor.init(m_maxPathResult))
+			return false;
+	}
+
+	for (int i = 0; i < m_maxAgents; ++i)
+	{
+		m_agentAnims[i].active = false;
+	}
+
+	// The navquery is mostly used for local searches, no need for large node pool.
+	m_navquery = dtAllocNavMeshQuery();
+	if (!m_navquery)
+		return false;
+	if (dtStatusFailed(m_navquery->init(nav, MAX_COMMON_NODES)))
+		return false;
+	
+	return true;
+}
+
+void dtCrowd::setObstacleAvoidanceParams(const int idx, const dtObstacleAvoidanceParams* params)
+{
+	if (idx >= 0 && idx < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS)
+		memcpy(&m_obstacleQueryParams[idx], params, sizeof(dtObstacleAvoidanceParams));
+}
+
+const dtObstacleAvoidanceParams* dtCrowd::getObstacleAvoidanceParams(const int idx) const
+{
+	if (idx >= 0 && idx < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS)
+		return &m_obstacleQueryParams[idx];
+	return 0;
+}
+
+int dtCrowd::getAgentCount() const
+{
+	return m_maxAgents;
+}
+
+/// @par
+/// 
+/// Agents in the pool may not be in use.  Check #dtCrowdAgent.active before using the returned object.
+const dtCrowdAgent* dtCrowd::getAgent(const int idx)
+{
+	if (idx < 0 || idx >= m_maxAgents)
+		return 0;
+	return &m_agents[idx];
+}
+
+/// 
+/// Agents in the pool may not be in use.  Check #dtCrowdAgent.active before using the returned object.
+dtCrowdAgent* dtCrowd::getEditableAgent(const int idx)
+{
+	if (idx < 0 || idx >= m_maxAgents)
+		return 0;
+	return &m_agents[idx];
+}
+
+void dtCrowd::updateAgentParameters(const int idx, const dtCrowdAgentParams* params)
+{
+	if (idx < 0 || idx >= m_maxAgents)
+		return;
+	memcpy(&m_agents[idx].params, params, sizeof(dtCrowdAgentParams));
+}
+
+/// @par
+///
+/// The agent's position will be constrained to the surface of the navigation mesh.
+int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
+{
+	// Find empty slot.
+	int idx = -1;
+	for (int i = 0; i < m_maxAgents; ++i)
+	{
+		if (!m_agents[i].active)
+		{
+			idx = i;
+			break;
+		}
+	}
+	if (idx == -1)
+		return -1;
+	
+	dtCrowdAgent* ag = &m_agents[idx];		
+
+	updateAgentParameters(idx, params);
+	
+	// Find nearest position on navmesh and place the agent there.
+	float nearest[3];
+	dtPolyRef ref = 0;
+	dtVcopy(nearest, pos);
+	dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest);
+	if (dtStatusFailed(status))
+	{
+		dtVcopy(nearest, pos);
+		ref = 0;
+	}
+	
+	ag->corridor.reset(ref, nearest);
+	ag->boundary.reset();
+	ag->partial = false;
+
+	ag->topologyOptTime = 0;
+	ag->targetReplanTime = 0;
+	ag->nneis = 0;
+	
+	dtVset(ag->dvel, 0,0,0);
+	dtVset(ag->nvel, 0,0,0);
+	dtVset(ag->vel, 0,0,0);
+	dtVcopy(ag->npos, nearest);
+	
+	ag->desiredSpeed = 0;
+
+	if (ref)
+		ag->state = DT_CROWDAGENT_STATE_WALKING;
+	else
+		ag->state = DT_CROWDAGENT_STATE_INVALID;
+	
+	ag->targetState = DT_CROWDAGENT_TARGET_NONE;
+	
+	ag->active = true;
+
+	return idx;
+}
+
+/// @par
+///
+/// The agent is deactivated and will no longer be processed.  Its #dtCrowdAgent object
+/// is not removed from the pool.  It is marked as inactive so that it is available for reuse.
+void dtCrowd::removeAgent(const int idx)
+{
+	if (idx >= 0 && idx < m_maxAgents)
+	{
+		m_agents[idx].active = false;
+	}
+}
+
+bool dtCrowd::requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos)
+{
+	if (idx < 0 || idx >= m_maxAgents)
+		return false;
+	
+	dtCrowdAgent* ag = &m_agents[idx];
+	
+	// Initialize request.
+	ag->targetRef = ref;
+	dtVcopy(ag->targetPos, pos);
+	ag->targetPathqRef = DT_PATHQ_INVALID;
+	ag->targetReplan = true;
+	if (ag->targetRef)
+		ag->targetState = DT_CROWDAGENT_TARGET_REQUESTING;
+	else
+		ag->targetState = DT_CROWDAGENT_TARGET_FAILED;
+	
+	return true;
+}
+
+/// @par
+/// 
+/// This method is used when a new target is set.
+/// 
+/// The position will be constrained to the surface of the navigation mesh.
+///
+/// The request will be processed during the next #update().
+bool dtCrowd::requestMoveTarget(const int idx, dtPolyRef ref, const float* pos)
+{
+	if (idx < 0 || idx >= m_maxAgents)
+		return false;
+	if (!ref)
+		return false;
+
+	dtCrowdAgent* ag = &m_agents[idx];
+	
+	// Initialize request.
+	ag->targetRef = ref;
+	dtVcopy(ag->targetPos, pos);
+	ag->targetPathqRef = DT_PATHQ_INVALID;
+	ag->targetReplan = false;
+	if (ag->targetRef)
+		ag->targetState = DT_CROWDAGENT_TARGET_REQUESTING;
+	else
+		ag->targetState = DT_CROWDAGENT_TARGET_FAILED;
+
+	return true;
+}
+
+bool dtCrowd::requestMoveVelocity(const int idx, const float* vel)
+{
+	if (idx < 0 || idx >= m_maxAgents)
+		return false;
+	
+	dtCrowdAgent* ag = &m_agents[idx];
+	
+	// Initialize request.
+	ag->targetRef = 0;
+	dtVcopy(ag->targetPos, vel);
+	ag->targetPathqRef = DT_PATHQ_INVALID;
+	ag->targetReplan = false;
+	ag->targetState = DT_CROWDAGENT_TARGET_VELOCITY;
+	
+	return true;
+}
+
+bool dtCrowd::resetMoveTarget(const int idx)
+{
+	if (idx < 0 || idx >= m_maxAgents)
+		return false;
+	
+	dtCrowdAgent* ag = &m_agents[idx];
+	
+	// Initialize request.
+	ag->targetRef = 0;
+	dtVset(ag->targetPos, 0,0,0);
+	ag->targetPathqRef = DT_PATHQ_INVALID;
+	ag->targetReplan = false;
+	ag->targetState = DT_CROWDAGENT_TARGET_NONE;
+	
+	return true;
+}
+
+int dtCrowd::getActiveAgents(dtCrowdAgent** agents, const int maxAgents)
+{
+	int n = 0;
+	for (int i = 0; i < m_maxAgents; ++i)
+	{
+		if (!m_agents[i].active) continue;
+		if (n < maxAgents)
+			agents[n++] = &m_agents[i];
+	}
+	return n;
+}
+
+
+void dtCrowd::updateMoveRequest(const float /*dt*/)
+{
+	const int PATH_MAX_AGENTS = 8;
+	dtCrowdAgent* queue[PATH_MAX_AGENTS];
+	int nqueue = 0;
+	
+	// Fire off new requests.
+	for (int i = 0; i < m_maxAgents; ++i)
+	{
+		dtCrowdAgent* ag = &m_agents[i];
+		if (!ag->active)
+			continue;
+		if (ag->state == DT_CROWDAGENT_STATE_INVALID)
+			continue;
+		if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+			continue;
+
+		if (ag->targetState == DT_CROWDAGENT_TARGET_REQUESTING)
+		{
+			const dtPolyRef* path = ag->corridor.getPath();
+			const int npath = ag->corridor.getPathCount();
+			dtAssert(npath);
+
+			static const int MAX_RES = 32;
+			float reqPos[3];
+			dtPolyRef reqPath[MAX_RES];	// The path to the request location
+			int reqPathCount = 0;
+
+			// Quick search towards the goal.
+			static const int MAX_ITER = 20;
+			m_navquery->initSlicedFindPath(path[0], ag->targetRef, ag->npos, ag->targetPos, &m_filters[ag->params.queryFilterType]);
+			m_navquery->updateSlicedFindPath(MAX_ITER, 0);
+			dtStatus status = 0;
+			if (ag->targetReplan) // && npath > 10)
+			{
+				// Try to use existing steady path during replan if possible.
+				status = m_navquery->finalizeSlicedFindPathPartial(path, npath, reqPath, &reqPathCount, MAX_RES);
+			}
+			else
+			{
+				// Try to move towards target when goal changes.
+				status = m_navquery->finalizeSlicedFindPath(reqPath, &reqPathCount, MAX_RES);
+			}
+
+			if (!dtStatusFailed(status) && reqPathCount > 0)
+			{
+				// In progress or succeed.
+				if (reqPath[reqPathCount-1] != ag->targetRef)
+				{
+					// Partial path, constrain target position inside the last polygon.
+					status = m_navquery->closestPointOnPoly(reqPath[reqPathCount-1], ag->targetPos, reqPos, 0);
+					if (dtStatusFailed(status))
+						reqPathCount = 0;
+				}
+				else
+				{
+					dtVcopy(reqPos, ag->targetPos);
+				}
+			}
+			else
+			{
+				reqPathCount = 0;
+			}
+				
+			if (!reqPathCount)
+			{
+				// Could not find path, start the request from current location.
+				dtVcopy(reqPos, ag->npos);
+				reqPath[0] = path[0];
+				reqPathCount = 1;
+			}
+
+			ag->corridor.setCorridor(reqPos, reqPath, reqPathCount);
+			ag->boundary.reset();
+			ag->partial = false;
+
+			if (reqPath[reqPathCount-1] == ag->targetRef)
+			{
+				ag->targetState = DT_CROWDAGENT_TARGET_VALID;
+				ag->targetReplanTime = 0.0;
+			}
+			else
+			{
+				// The path is longer or potentially unreachable, full plan.
+				ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE;
+			}
+		}
+		
+		if (ag->targetState == DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE)
+		{
+			nqueue = addToPathQueue(ag, queue, nqueue, PATH_MAX_AGENTS);
+		}
+	}
+
+	for (int i = 0; i < nqueue; ++i)
+	{
+		dtCrowdAgent* ag = queue[i];
+		ag->targetPathqRef = m_pathq.request(ag->corridor.getLastPoly(), ag->targetRef,
+											 ag->corridor.getTarget(), ag->targetPos, &m_filters[ag->params.queryFilterType]);
+		if (ag->targetPathqRef != DT_PATHQ_INVALID)
+			ag->targetState = DT_CROWDAGENT_TARGET_WAITING_FOR_PATH;
+	}
+
+	
+	// Update requests.
+	m_pathq.update(MAX_ITERS_PER_UPDATE);
+
+	dtStatus status;
+
+	// Process path results.
+	for (int i = 0; i < m_maxAgents; ++i)
+	{
+		dtCrowdAgent* ag = &m_agents[i];
+		if (!ag->active)
+			continue;
+		if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+			continue;
+		
+		if (ag->targetState == DT_CROWDAGENT_TARGET_WAITING_FOR_PATH)
+		{
+			// Poll path queue.
+			status = m_pathq.getRequestStatus(ag->targetPathqRef);
+			if (dtStatusFailed(status))
+			{
+				// Path find failed, retry if the target location is still valid.
+				ag->targetPathqRef = DT_PATHQ_INVALID;
+				if (ag->targetRef)
+					ag->targetState = DT_CROWDAGENT_TARGET_REQUESTING;
+				else
+					ag->targetState = DT_CROWDAGENT_TARGET_FAILED;
+				ag->targetReplanTime = 0.0;
+			}
+			else if (dtStatusSucceed(status))
+			{
+				const dtPolyRef* path = ag->corridor.getPath();
+				const int npath = ag->corridor.getPathCount();
+				dtAssert(npath);
+				
+				// Apply results.
+				float targetPos[3];
+				dtVcopy(targetPos, ag->targetPos);
+				
+				dtPolyRef* res = m_pathResult;
+				bool valid = true;
+				int nres = 0;
+				status = m_pathq.getPathResult(ag->targetPathqRef, res, &nres, m_maxPathResult);
+				if (dtStatusFailed(status) || !nres)
+					valid = false;
+
+				if (dtStatusDetail(status, DT_PARTIAL_RESULT))
+					ag->partial = true;
+				else
+					ag->partial = false;
+
+				// Merge result and existing path.
+				// The agent might have moved whilst the request is
+				// being processed, so the path may have changed.
+				// We assume that the end of the path is at the same location
+				// where the request was issued.
+				
+				// The last ref in the old path should be the same as
+				// the location where the request was issued..
+				if (valid && path[npath-1] != res[0])
+					valid = false;
+				
+				if (valid)
+				{
+					// Put the old path infront of the old path.
+					if (npath > 1)
+					{
+						// Make space for the old path.
+						if ((npath-1)+nres > m_maxPathResult)
+							nres = m_maxPathResult - (npath-1);
+						
+						memmove(res+npath-1, res, sizeof(dtPolyRef)*nres);
+						// Copy old path in the beginning.
+						memcpy(res, path, sizeof(dtPolyRef)*(npath-1));
+						nres += npath-1;
+						
+						// Remove trackbacks
+						for (int j = 0; j < nres; ++j)
+						{
+							if (j-1 >= 0 && j+1 < nres)
+							{
+								if (res[j-1] == res[j+1])
+								{
+									memmove(res+(j-1), res+(j+1), sizeof(dtPolyRef)*(nres-(j+1)));
+									nres -= 2;
+									j -= 2;
+								}
+							}
+						}
+						
+					}
+					
+					// Check for partial path.
+					if (res[nres-1] != ag->targetRef)
+					{
+						// Partial path, constrain target position inside the last polygon.
+						float nearest[3];
+						status = m_navquery->closestPointOnPoly(res[nres-1], targetPos, nearest, 0);
+						if (dtStatusSucceed(status))
+							dtVcopy(targetPos, nearest);
+						else
+							valid = false;
+					}
+				}
+				
+				if (valid)
+				{
+					// Set current corridor.
+					ag->corridor.setCorridor(targetPos, res, nres);
+					// Force to update boundary.
+					ag->boundary.reset();
+					ag->targetState = DT_CROWDAGENT_TARGET_VALID;
+				}
+				else
+				{
+					// Something went wrong.
+					ag->targetState = DT_CROWDAGENT_TARGET_FAILED;
+				}
+
+				ag->targetReplanTime = 0.0;
+			}
+		}
+	}
+	
+}
+
+
+void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagents, const float dt)
+{
+	if (!nagents)
+		return;
+	
+	const float OPT_TIME_THR = 0.5f; // seconds
+	const int OPT_MAX_AGENTS = 1;
+	dtCrowdAgent* queue[OPT_MAX_AGENTS];
+	int nqueue = 0;
+	
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+		if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+			continue;
+		if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_TOPO) == 0)
+			continue;
+		ag->topologyOptTime += dt;
+		if (ag->topologyOptTime >= OPT_TIME_THR)
+			nqueue = addToOptQueue(ag, queue, nqueue, OPT_MAX_AGENTS);
+	}
+
+	for (int i = 0; i < nqueue; ++i)
+	{
+		dtCrowdAgent* ag = queue[i];
+		ag->corridor.optimizePathTopology(m_navquery, &m_filters[ag->params.queryFilterType]);
+		ag->topologyOptTime = 0;
+	}
+
+}
+
+void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const float dt)
+{
+	static const int CHECK_LOOKAHEAD = 10;
+	static const float TARGET_REPLAN_DELAY = 1.0; // seconds
+	
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+			
+		ag->targetReplanTime += dt;
+
+		bool replan = false;
+
+		// First check that the current location is valid.
+		const int idx = getAgentIndex(ag);
+		float agentPos[3];
+		dtPolyRef agentRef = ag->corridor.getFirstPoly();
+		dtVcopy(agentPos, ag->npos);
+		if (!m_navquery->isValidPolyRef(agentRef, &m_filters[ag->params.queryFilterType]))
+		{
+			// Current location is not valid, try to reposition.
+			// TODO: this can snap agents, how to handle that?
+			float nearest[3];
+			dtVcopy(nearest, agentPos);
+			agentRef = 0;
+			m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
+			dtVcopy(agentPos, nearest);
+
+			if (!agentRef)
+			{
+				// Could not find location in navmesh, set state to invalid.
+				ag->corridor.reset(0, agentPos);
+				ag->partial = false;
+				ag->boundary.reset();
+				ag->state = DT_CROWDAGENT_STATE_INVALID;
+				continue;
+			}
+
+			// Make sure the first polygon is valid, but leave other valid
+			// polygons in the path so that replanner can adjust the path better.
+			ag->corridor.fixPathStart(agentRef, agentPos);
+//			ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
+			ag->boundary.reset();
+			dtVcopy(ag->npos, agentPos);
+
+			replan = true;
+		}
+
+		// If the agent does not have move target or is controlled by velocity, no need to recover the target nor replan.
+		if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+			continue;
+
+		// Try to recover move request position.
+		if (ag->targetState != DT_CROWDAGENT_TARGET_NONE && ag->targetState != DT_CROWDAGENT_TARGET_FAILED)
+		{
+			if (!m_navquery->isValidPolyRef(ag->targetRef, &m_filters[ag->params.queryFilterType]))
+			{
+				// Current target is not valid, try to reposition.
+				float nearest[3];
+				dtVcopy(nearest, ag->targetPos);
+				ag->targetRef = 0;
+				m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
+				dtVcopy(ag->targetPos, nearest);
+				replan = true;
+			}
+			if (!ag->targetRef)
+			{
+				// Failed to reposition target, fail moverequest.
+				ag->corridor.reset(agentRef, agentPos);
+				ag->partial = false;
+				ag->targetState = DT_CROWDAGENT_TARGET_NONE;
+			}
+		}
+
+		// If nearby corridor is not valid, replan.
+		if (!ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filters[ag->params.queryFilterType]))
+		{
+			// Fix current path.
+//			ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
+//			ag->boundary.reset();
+			replan = true;
+		}
+		
+		// If the end of the path is near and it is not the requested location, replan.
+		if (ag->targetState == DT_CROWDAGENT_TARGET_VALID)
+		{
+			if (ag->targetReplanTime > TARGET_REPLAN_DELAY &&
+				ag->corridor.getPathCount() < CHECK_LOOKAHEAD &&
+				ag->corridor.getLastPoly() != ag->targetRef)
+				replan = true;
+		}
+
+		// Try to replan path to goal.
+		if (replan)
+		{
+			if (ag->targetState != DT_CROWDAGENT_TARGET_NONE)
+			{
+				requestMoveTargetReplan(idx, ag->targetRef, ag->targetPos);
+			}
+		}
+	}
+}
+	
+void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
+{
+	m_velocitySampleCount = 0;
+	
+	const int debugIdx = debug ? debug->idx : -1;
+	
+	dtCrowdAgent** agents = m_activeAgents;
+	int nagents = getActiveAgents(agents, m_maxAgents);
+
+	// Check that all agents still have valid paths.
+	checkPathValidity(agents, nagents, dt);
+	
+	// Update async move request and path finder.
+	updateMoveRequest(dt);
+
+	// Optimize path topology.
+	updateTopologyOptimization(agents, nagents, dt);
+	
+	// Register agents to proximity grid.
+	m_grid->clear();
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		const float* p = ag->npos;
+		const float r = ag->params.radius;
+		m_grid->addItem((unsigned short)i, p[0]-r, p[2]-r, p[0]+r, p[2]+r);
+	}
+	
+	// Get nearby navmesh segments and agents to collide with.
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+
+		// Update the collision boundary after certain distance has been passed or
+		// if it has become invalid.
+		const float updateThr = ag->params.collisionQueryRange*0.25f;
+		if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) ||
+			!ag->boundary.isValid(m_navquery, &m_filters[ag->params.queryFilterType]))
+		{
+			ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange,
+								m_navquery, &m_filters[ag->params.queryFilterType]);
+		}
+		// Query neighbour agents
+		ag->nneis = getNeighbours(ag->npos, ag->params.height, ag->params.collisionQueryRange,
+								  ag, ag->neis, DT_CROWDAGENT_MAX_NEIGHBOURS,
+								  agents, nagents, m_grid);
+		for (int j = 0; j < ag->nneis; j++)
+			ag->neis[j].idx = getAgentIndex(agents[ag->neis[j].idx]);
+	}
+	
+	// Find next corner to steer to.
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+		if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+			continue;
+		
+		// Find corners for steering
+		ag->ncorners = ag->corridor.findCorners(ag->cornerVerts, ag->cornerFlags, ag->cornerPolys,
+												DT_CROWDAGENT_MAX_CORNERS, m_navquery, &m_filters[ag->params.queryFilterType]);
+		
+		// Check to see if the corner after the next corner is directly visible,
+		// and short cut to there.
+		if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_VIS) && ag->ncorners > 0)
+		{
+			const float* target = &ag->cornerVerts[dtMin(1,ag->ncorners-1)*3];
+			ag->corridor.optimizePathVisibility(target, ag->params.pathOptimizationRange, m_navquery, &m_filters[ag->params.queryFilterType]);
+			
+			// Copy data for debug purposes.
+			if (debugIdx == i)
+			{
+				dtVcopy(debug->optStart, ag->corridor.getPos());
+				dtVcopy(debug->optEnd, target);
+			}
+		}
+		else
+		{
+			// Copy data for debug purposes.
+			if (debugIdx == i)
+			{
+				dtVset(debug->optStart, 0,0,0);
+				dtVset(debug->optEnd, 0,0,0);
+			}
+		}
+	}
+	
+	// Trigger off-mesh connections (depends on corners).
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+		if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+			continue;
+		
+		// Check 
+		const float triggerRadius = ag->params.radius*2.25f;
+		if (overOffmeshConnection(ag, triggerRadius))
+		{
+			// Prepare to off-mesh connection.
+			const int idx = (int)(ag - m_agents);
+			dtCrowdAgentAnimation* anim = &m_agentAnims[idx];
+			
+			// Adjust the path over the off-mesh connection.
+			dtPolyRef refs[2];
+			if (ag->corridor.moveOverOffmeshConnection(ag->cornerPolys[ag->ncorners-1], refs,
+													   anim->startPos, anim->endPos, m_navquery))
+			{
+				dtVcopy(anim->initPos, ag->npos);
+				anim->polyRef = refs[1];
+				anim->active = true;
+				anim->t = 0.0f;
+				anim->tmax = (dtVdist2D(anim->startPos, anim->endPos) / ag->params.maxSpeed) * 0.5f;
+				
+				ag->state = DT_CROWDAGENT_STATE_OFFMESH;
+				ag->ncorners = 0;
+				ag->nneis = 0;
+				continue;
+			}
+			else
+			{
+				// Path validity check will ensure that bad/blocked connections will be replanned.
+			}
+		}
+	}
+		
+	// Calculate steering.
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+		if (ag->targetState == DT_CROWDAGENT_TARGET_NONE)
+			continue;
+		
+		float dvel[3] = {0,0,0};
+
+		if (ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+		{
+			dtVcopy(dvel, ag->targetPos);
+			ag->desiredSpeed = dtVlen(ag->targetPos);
+		}
+		else
+		{
+			// Calculate steering direction.
+			if (ag->params.updateFlags & DT_CROWD_ANTICIPATE_TURNS)
+				calcSmoothSteerDirection(ag, dvel);
+			else
+				calcStraightSteerDirection(ag, dvel);
+			
+			// Calculate speed scale, which tells the agent to slowdown at the end of the path.
+			const float slowDownRadius = ag->params.radius*2;	// TODO: make less hacky.
+			const float speedScale = getDistanceToGoal(ag, slowDownRadius) / slowDownRadius;
+				
+			ag->desiredSpeed = ag->params.maxSpeed;
+			dtVscale(dvel, dvel, ag->desiredSpeed * speedScale);
+		}
+
+		// Separation
+		if (ag->params.updateFlags & DT_CROWD_SEPARATION)
+		{
+			const float separationDist = ag->params.collisionQueryRange; 
+			const float invSeparationDist = 1.0f / separationDist; 
+			const float separationWeight = ag->params.separationWeight;
+			
+			float w = 0;
+			float disp[3] = {0,0,0};
+			
+			for (int j = 0; j < ag->nneis; ++j)
+			{
+				const dtCrowdAgent* nei = &m_agents[ag->neis[j].idx];
+				
+				float diff[3];
+				dtVsub(diff, ag->npos, nei->npos);
+				diff[1] = 0;
+				
+				const float distSqr = dtVlenSqr(diff);
+				if (distSqr < 0.00001f)
+					continue;
+				if (distSqr > dtSqr(separationDist))
+					continue;
+				const float dist = dtMathSqrtf(distSqr);
+				const float weight = separationWeight * (1.0f - dtSqr(dist*invSeparationDist));
+				
+				dtVmad(disp, disp, diff, weight/dist);
+				w += 1.0f;
+			}
+			
+			if (w > 0.0001f)
+			{
+				// Adjust desired velocity.
+				dtVmad(dvel, dvel, disp, 1.0f/w);
+				// Clamp desired velocity to desired speed.
+				const float speedSqr = dtVlenSqr(dvel);
+				const float desiredSqr = dtSqr(ag->desiredSpeed);
+				if (speedSqr > desiredSqr)
+					dtVscale(dvel, dvel, desiredSqr/speedSqr);
+			}
+		}
+		
+		// Set the desired velocity.
+		dtVcopy(ag->dvel, dvel);
+	}
+	
+	// Velocity planning.	
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+		
+		if (ag->params.updateFlags & DT_CROWD_OBSTACLE_AVOIDANCE)
+		{
+			m_obstacleQuery->reset();
+			
+			// Add neighbours as obstacles.
+			for (int j = 0; j < ag->nneis; ++j)
+			{
+				const dtCrowdAgent* nei = &m_agents[ag->neis[j].idx];
+				m_obstacleQuery->addCircle(nei->npos, nei->params.radius, nei->vel, nei->dvel);
+			}
+
+			// Append neighbour segments as obstacles.
+			for (int j = 0; j < ag->boundary.getSegmentCount(); ++j)
+			{
+				const float* s = ag->boundary.getSegment(j);
+				if (dtTriArea2D(ag->npos, s, s+3) < 0.0f)
+					continue;
+				m_obstacleQuery->addSegment(s, s+3);
+			}
+
+			dtObstacleAvoidanceDebugData* vod = 0;
+			if (debugIdx == i) 
+				vod = debug->vod;
+			
+			// Sample new safe velocity.
+			bool adaptive = true;
+			int ns = 0;
+
+			const dtObstacleAvoidanceParams* params = &m_obstacleQueryParams[ag->params.obstacleAvoidanceType];
+				
+			if (adaptive)
+			{
+				ns = m_obstacleQuery->sampleVelocityAdaptive(ag->npos, ag->params.radius, ag->desiredSpeed,
+															 ag->vel, ag->dvel, ag->nvel, params, vod);
+			}
+			else
+			{
+				ns = m_obstacleQuery->sampleVelocityGrid(ag->npos, ag->params.radius, ag->desiredSpeed,
+														 ag->vel, ag->dvel, ag->nvel, params, vod);
+			}
+			m_velocitySampleCount += ns;
+		}
+		else
+		{
+			// If not using velocity planning, new velocity is directly the desired velocity.
+			dtVcopy(ag->nvel, ag->dvel);
+		}
+	}
+
+	// Integrate.
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+		integrate(ag, dt);
+	}
+	
+	// Handle collisions.
+	static const float COLLISION_RESOLVE_FACTOR = 0.7f;
+	
+	for (int iter = 0; iter < 4; ++iter)
+	{
+		for (int i = 0; i < nagents; ++i)
+		{
+			dtCrowdAgent* ag = agents[i];
+			const int idx0 = getAgentIndex(ag);
+			
+			if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+				continue;
+
+			dtVset(ag->disp, 0,0,0);
+			
+			float w = 0;
+
+			for (int j = 0; j < ag->nneis; ++j)
+			{
+				const dtCrowdAgent* nei = &m_agents[ag->neis[j].idx];
+				const int idx1 = getAgentIndex(nei);
+
+				float diff[3];
+				dtVsub(diff, ag->npos, nei->npos);
+				diff[1] = 0;
+				
+				float dist = dtVlenSqr(diff);
+				if (dist > dtSqr(ag->params.radius + nei->params.radius))
+					continue;
+				dist = dtMathSqrtf(dist);
+				float pen = (ag->params.radius + nei->params.radius) - dist;
+				if (dist < 0.0001f)
+				{
+					// Agents on top of each other, try to choose diverging separation directions.
+					if (idx0 > idx1)
+						dtVset(diff, -ag->dvel[2],0,ag->dvel[0]);
+					else
+						dtVset(diff, ag->dvel[2],0,-ag->dvel[0]);
+					pen = 0.01f;
+				}
+				else
+				{
+					pen = (1.0f/dist) * (pen*0.5f) * COLLISION_RESOLVE_FACTOR;
+				}
+				
+				dtVmad(ag->disp, ag->disp, diff, pen);			
+				
+				w += 1.0f;
+			}
+			
+			if (w > 0.0001f)
+			{
+				const float iw = 1.0f / w;
+				dtVscale(ag->disp, ag->disp, iw);
+			}
+		}
+		
+		for (int i = 0; i < nagents; ++i)
+		{
+			dtCrowdAgent* ag = agents[i];
+			if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+				continue;
+			
+			dtVadd(ag->npos, ag->npos, ag->disp);
+		}
+	}
+	
+	for (int i = 0; i < nagents; ++i)
+	{
+		dtCrowdAgent* ag = agents[i];
+		if (ag->state != DT_CROWDAGENT_STATE_WALKING)
+			continue;
+		
+		// Move along navmesh.
+		ag->corridor.movePosition(ag->npos, m_navquery, &m_filters[ag->params.queryFilterType]);
+		// Get valid constrained position back.
+		dtVcopy(ag->npos, ag->corridor.getPos());
+
+		// If not using path, truncate the corridor to just one poly.
+		if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
+		{
+			ag->corridor.reset(ag->corridor.getFirstPoly(), ag->npos);
+			ag->partial = false;
+		}
+
+	}
+	
+	// Update agents using off-mesh connection.
+	for (int i = 0; i < m_maxAgents; ++i)
+	{
+		dtCrowdAgentAnimation* anim = &m_agentAnims[i];
+		if (!anim->active)
+			continue;
+		dtCrowdAgent* ag = agents[i];
+
+		anim->t += dt;
+		if (anim->t > anim->tmax)
+		{
+			// Reset animation
+			anim->active = false;
+			// Prepare agent for walking.
+			ag->state = DT_CROWDAGENT_STATE_WALKING;
+			continue;
+		}
+		
+		// Update position
+		const float ta = anim->tmax*0.15f;
+		const float tb = anim->tmax;
+		if (anim->t < ta)
+		{
+			const float u = tween(anim->t, 0.0, ta);
+			dtVlerp(ag->npos, anim->initPos, anim->startPos, u);
+		}
+		else
+		{
+			const float u = tween(anim->t, ta, tb);
+			dtVlerp(ag->npos, anim->startPos, anim->endPos, u);
+		}
+			
+		// Update velocity.
+		dtVset(ag->vel, 0,0,0);
+		dtVset(ag->dvel, 0,0,0);
+	}
+	
+}

+ 137 - 0
Source/ThirdParty/DetourCrowd/source/DetourLocalBoundary.cpp

@@ -0,0 +1,137 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#include <string.h>
+#include "DetourLocalBoundary.h"
+#include "DetourNavMeshQuery.h"
+#include "DetourCommon.h"
+#include "DetourAssert.h"
+
+
+dtLocalBoundary::dtLocalBoundary() :
+	m_nsegs(0),
+	m_npolys(0)
+{
+	dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
+}
+
+dtLocalBoundary::~dtLocalBoundary()
+{
+}
+
+void dtLocalBoundary::reset()
+{
+	dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
+	m_npolys = 0;
+	m_nsegs = 0;
+}
+
+void dtLocalBoundary::addSegment(const float dist, const float* s)
+{
+	// Insert neighbour based on the distance.
+	Segment* seg = 0;
+	if (!m_nsegs)
+	{
+		// First, trivial accept.
+		seg = &m_segs[0];
+	}
+	else if (dist >= m_segs[m_nsegs-1].d)
+	{
+		// Further than the last segment, skip.
+		if (m_nsegs >= MAX_LOCAL_SEGS)
+			return;
+		// Last, trivial accept.
+		seg = &m_segs[m_nsegs];
+	}
+	else
+	{
+		// Insert inbetween.
+		int i;
+		for (i = 0; i < m_nsegs; ++i)
+			if (dist <= m_segs[i].d)
+				break;
+		const int tgt = i+1;
+		const int n = dtMin(m_nsegs-i, MAX_LOCAL_SEGS-tgt);
+		dtAssert(tgt+n <= MAX_LOCAL_SEGS);
+		if (n > 0)
+			memmove(&m_segs[tgt], &m_segs[i], sizeof(Segment)*n);
+		seg = &m_segs[i];
+	}
+	
+	seg->d = dist;
+	memcpy(seg->s, s, sizeof(float)*6);
+	
+	if (m_nsegs < MAX_LOCAL_SEGS)
+		m_nsegs++;
+}
+
+void dtLocalBoundary::update(dtPolyRef ref, const float* pos, const float collisionQueryRange,
+							 dtNavMeshQuery* navquery, const dtQueryFilter* filter)
+{
+	static const int MAX_SEGS_PER_POLY = DT_VERTS_PER_POLYGON*3;
+	
+	if (!ref)
+	{
+		dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
+		m_nsegs = 0;
+		m_npolys = 0;
+		return;
+	}
+	
+	dtVcopy(m_center, pos);
+	
+	// First query non-overlapping polygons.
+	navquery->findLocalNeighbourhood(ref, pos, collisionQueryRange,
+									 filter, m_polys, 0, &m_npolys, MAX_LOCAL_POLYS);
+	
+	// Secondly, store all polygon edges.
+	m_nsegs = 0;
+	float segs[MAX_SEGS_PER_POLY*6];
+	int nsegs = 0;
+	for (int j = 0; j < m_npolys; ++j)
+	{
+		navquery->getPolyWallSegments(m_polys[j], filter, segs, 0, &nsegs, MAX_SEGS_PER_POLY);
+		for (int k = 0; k < nsegs; ++k)
+		{
+			const float* s = &segs[k*6];
+			// Skip too distant segments.
+			float tseg;
+			const float distSqr = dtDistancePtSegSqr2D(pos, s, s+3, tseg);
+			if (distSqr > dtSqr(collisionQueryRange))
+				continue;
+			addSegment(distSqr, s);
+		}
+	}
+}
+
+bool dtLocalBoundary::isValid(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
+{
+	if (!m_npolys)
+		return false;
+	
+	// Check that all polygons still pass query filter.
+	for (int i = 0; i < m_npolys; ++i)
+	{
+		if (!navquery->isValidPolyRef(m_polys[i], filter))
+			return false;
+	}
+	
+	return true;
+}
+

+ 544 - 0
Source/ThirdParty/DetourCrowd/source/DetourObstacleAvoidance.cpp

@@ -0,0 +1,544 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include "DetourObstacleAvoidance.h"
+#include "DetourCommon.h"
+#include "DetourMath.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <string.h>
+#include <float.h>
+#include <new>
+
+static const float DT_PI = 3.14159265f;
+
+static int sweepCircleCircle(const float* c0, const float r0, const float* v,
+							 const float* c1, const float r1,
+							 float& tmin, float& tmax)
+{
+	static const float EPS = 0.0001f;
+	float s[3];
+	dtVsub(s,c1,c0);
+	float r = r0+r1;
+	float c = dtVdot2D(s,s) - r*r;
+	float a = dtVdot2D(v,v);
+	if (a < EPS) return 0;	// not moving
+	
+	// Overlap, calc time to exit.
+	float b = dtVdot2D(v,s);
+	float d = b*b - a*c;
+	if (d < 0.0f) return 0; // no intersection.
+	a = 1.0f / a;
+	const float rd = dtSqrt(d);
+	tmin = (b - rd) * a;
+	tmax = (b + rd) * a;
+	return 1;
+}
+
+static int isectRaySeg(const float* ap, const float* u,
+					   const float* bp, const float* bq,
+					   float& t)
+{
+	float v[3], w[3];
+	dtVsub(v,bq,bp);
+	dtVsub(w,ap,bp);
+	float d = dtVperp2D(u,v);
+	if (dtMathFabs(d) < 1e-6f) return 0;
+	d = 1.0f/d;
+	t = dtVperp2D(v,w) * d;
+	if (t < 0 || t > 1) return 0;
+	float s = dtVperp2D(u,w) * d;
+	if (s < 0 || s > 1) return 0;
+	return 1;
+}
+
+
+
+dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData()
+{
+	void* mem = dtAlloc(sizeof(dtObstacleAvoidanceDebugData), DT_ALLOC_PERM);
+	if (!mem) return 0;
+	return new(mem) dtObstacleAvoidanceDebugData;
+}
+
+void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr)
+{
+	if (!ptr) return;
+	ptr->~dtObstacleAvoidanceDebugData();
+	dtFree(ptr);
+}
+
+
+dtObstacleAvoidanceDebugData::dtObstacleAvoidanceDebugData() :
+	m_nsamples(0),
+	m_maxSamples(0),
+	m_vel(0),
+	m_ssize(0),
+	m_pen(0),
+	m_vpen(0),
+	m_vcpen(0),
+	m_spen(0),
+	m_tpen(0)
+{
+}
+
+dtObstacleAvoidanceDebugData::~dtObstacleAvoidanceDebugData()
+{
+	dtFree(m_vel);
+	dtFree(m_ssize);
+	dtFree(m_pen);
+	dtFree(m_vpen);
+	dtFree(m_vcpen);
+	dtFree(m_spen);
+	dtFree(m_tpen);
+}
+		
+bool dtObstacleAvoidanceDebugData::init(const int maxSamples)
+{
+	dtAssert(maxSamples);
+	m_maxSamples = maxSamples;
+
+	m_vel = (float*)dtAlloc(sizeof(float)*3*m_maxSamples, DT_ALLOC_PERM);
+	if (!m_vel)
+		return false;
+	m_pen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+	if (!m_pen)
+		return false;
+	m_ssize = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+	if (!m_ssize)
+		return false;
+	m_vpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+	if (!m_vpen)
+		return false;
+	m_vcpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+	if (!m_vcpen)
+		return false;
+	m_spen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+	if (!m_spen)
+		return false;
+	m_tpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+	if (!m_tpen)
+		return false;
+	
+	return true;
+}
+
+void dtObstacleAvoidanceDebugData::reset()
+{
+	m_nsamples = 0;
+}
+
+void dtObstacleAvoidanceDebugData::addSample(const float* vel, const float ssize, const float pen,
+											 const float vpen, const float vcpen, const float spen, const float tpen)
+{
+	if (m_nsamples >= m_maxSamples)
+		return;
+	dtAssert(m_vel);
+	dtAssert(m_ssize);
+	dtAssert(m_pen);
+	dtAssert(m_vpen);
+	dtAssert(m_vcpen);
+	dtAssert(m_spen);
+	dtAssert(m_tpen);
+	dtVcopy(&m_vel[m_nsamples*3], vel);
+	m_ssize[m_nsamples] = ssize;
+	m_pen[m_nsamples] = pen;
+	m_vpen[m_nsamples] = vpen;
+	m_vcpen[m_nsamples] = vcpen;
+	m_spen[m_nsamples] = spen;
+	m_tpen[m_nsamples] = tpen;
+	m_nsamples++;
+}
+
+static void normalizeArray(float* arr, const int n)
+{
+	// Normalize penaly range.
+	float minPen = FLT_MAX;
+	float maxPen = -FLT_MAX;
+	for (int i = 0; i < n; ++i)
+	{
+		minPen = dtMin(minPen, arr[i]);
+		maxPen = dtMax(maxPen, arr[i]);
+	}
+	const float penRange = maxPen-minPen;
+	const float s = penRange > 0.001f ? (1.0f / penRange) : 1;
+	for (int i = 0; i < n; ++i)
+		arr[i] = dtClamp((arr[i]-minPen)*s, 0.0f, 1.0f);
+}
+
+void dtObstacleAvoidanceDebugData::normalizeSamples()
+{
+	normalizeArray(m_pen, m_nsamples);
+	normalizeArray(m_vpen, m_nsamples);
+	normalizeArray(m_vcpen, m_nsamples);
+	normalizeArray(m_spen, m_nsamples);
+	normalizeArray(m_tpen, m_nsamples);
+}
+
+
+dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery()
+{
+	void* mem = dtAlloc(sizeof(dtObstacleAvoidanceQuery), DT_ALLOC_PERM);
+	if (!mem) return 0;
+	return new(mem) dtObstacleAvoidanceQuery;
+}
+
+void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr)
+{
+	if (!ptr) return;
+	ptr->~dtObstacleAvoidanceQuery();
+	dtFree(ptr);
+}
+
+
+dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() :
+	m_maxCircles(0),
+	m_circles(0),
+	m_ncircles(0),
+	m_maxSegments(0),
+	m_segments(0),
+	m_nsegments(0)
+{
+}
+
+dtObstacleAvoidanceQuery::~dtObstacleAvoidanceQuery()
+{
+	dtFree(m_circles);
+	dtFree(m_segments);
+}
+
+bool dtObstacleAvoidanceQuery::init(const int maxCircles, const int maxSegments)
+{
+	m_maxCircles = maxCircles;
+	m_ncircles = 0;
+	m_circles = (dtObstacleCircle*)dtAlloc(sizeof(dtObstacleCircle)*m_maxCircles, DT_ALLOC_PERM);
+	if (!m_circles)
+		return false;
+	memset(m_circles, 0, sizeof(dtObstacleCircle)*m_maxCircles);
+
+	m_maxSegments = maxSegments;
+	m_nsegments = 0;
+	m_segments = (dtObstacleSegment*)dtAlloc(sizeof(dtObstacleSegment)*m_maxSegments, DT_ALLOC_PERM);
+	if (!m_segments)
+		return false;
+	memset(m_segments, 0, sizeof(dtObstacleSegment)*m_maxSegments);
+	
+	return true;
+}
+
+void dtObstacleAvoidanceQuery::reset()
+{
+	m_ncircles = 0;
+	m_nsegments = 0;
+}
+
+void dtObstacleAvoidanceQuery::addCircle(const float* pos, const float rad,
+										 const float* vel, const float* dvel)
+{
+	if (m_ncircles >= m_maxCircles)
+		return;
+		
+	dtObstacleCircle* cir = &m_circles[m_ncircles++];
+	dtVcopy(cir->p, pos);
+	cir->rad = rad;
+	dtVcopy(cir->vel, vel);
+	dtVcopy(cir->dvel, dvel);
+}
+
+void dtObstacleAvoidanceQuery::addSegment(const float* p, const float* q)
+{
+	if (m_nsegments > m_maxSegments)
+		return;
+	
+	dtObstacleSegment* seg = &m_segments[m_nsegments++];
+	dtVcopy(seg->p, p);
+	dtVcopy(seg->q, q);
+}
+
+void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel)
+{
+	// Prepare obstacles
+	for (int i = 0; i < m_ncircles; ++i)
+	{
+		dtObstacleCircle* cir = &m_circles[i];
+		
+		// Side
+		const float* pa = pos;
+		const float* pb = cir->p;
+		
+		const float orig[3] = {0,0};
+		float dv[3];
+		dtVsub(cir->dp,pb,pa);
+		dtVnormalize(cir->dp);
+		dtVsub(dv, cir->dvel, dvel);
+		
+		const float a = dtTriArea2D(orig, cir->dp,dv);
+		if (a < 0.01f)
+		{
+			cir->np[0] = -cir->dp[2];
+			cir->np[2] = cir->dp[0];
+		}
+		else
+		{
+			cir->np[0] = cir->dp[2];
+			cir->np[2] = -cir->dp[0];
+		}
+	}	
+
+	for (int i = 0; i < m_nsegments; ++i)
+	{
+		dtObstacleSegment* seg = &m_segments[i];
+		
+		// Precalc if the agent is really close to the segment.
+		const float r = 0.01f;
+		float t;
+		seg->touch = dtDistancePtSegSqr2D(pos, seg->p, seg->q, t) < dtSqr(r);
+	}	
+}
+
+float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs,
+											  const float* pos, const float rad,
+											  const float* vel, const float* dvel,
+											  dtObstacleAvoidanceDebugData* debug)
+{
+	// Find min time of impact and exit amongst all obstacles.
+	float tmin = m_params.horizTime;
+	float side = 0;
+	int nside = 0;
+	
+	for (int i = 0; i < m_ncircles; ++i)
+	{
+		const dtObstacleCircle* cir = &m_circles[i];
+			
+		// RVO
+		float vab[3];
+		dtVscale(vab, vcand, 2);
+		dtVsub(vab, vab, vel);
+		dtVsub(vab, vab, cir->vel);
+		
+		// Side
+		side += dtClamp(dtMin(dtVdot2D(cir->dp,vab)*0.5f+0.5f, dtVdot2D(cir->np,vab)*2), 0.0f, 1.0f);
+		nside++;
+		
+		float htmin = 0, htmax = 0;
+		if (!sweepCircleCircle(pos,rad, vab, cir->p,cir->rad, htmin, htmax))
+			continue;
+		
+		// Handle overlapping obstacles.
+		if (htmin < 0.0f && htmax > 0.0f)
+		{
+			// Avoid more when overlapped.
+			htmin = -htmin * 0.5f;
+		}
+		
+		if (htmin >= 0.0f)
+		{
+			// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
+			if (htmin < tmin)
+				tmin = htmin;
+		}
+	}
+
+	for (int i = 0; i < m_nsegments; ++i)
+	{
+		const dtObstacleSegment* seg = &m_segments[i];
+		float htmin = 0;
+		
+		if (seg->touch)
+		{
+			// Special case when the agent is very close to the segment.
+			float sdir[3], snorm[3];
+			dtVsub(sdir, seg->q, seg->p);
+			snorm[0] = -sdir[2];
+			snorm[2] = sdir[0];
+			// If the velocity is pointing towards the segment, no collision.
+			if (dtVdot2D(snorm, vcand) < 0.0f)
+				continue;
+			// Else immediate collision.
+			htmin = 0.0f;
+		}
+		else
+		{
+			if (!isectRaySeg(pos, vcand, seg->p, seg->q, htmin))
+				continue;
+		}
+		
+		// Avoid less when facing walls.
+		htmin *= 2.0f;
+		
+		// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
+		if (htmin < tmin)
+			tmin = htmin;
+	}
+	
+	// Normalize side bias, to prevent it dominating too much.
+	if (nside)
+		side /= nside;
+	
+	const float vpen = m_params.weightDesVel * (dtVdist2D(vcand, dvel) * m_invVmax);
+	const float vcpen = m_params.weightCurVel * (dtVdist2D(vcand, vel) * m_invVmax);
+	const float spen = m_params.weightSide * side;
+	const float tpen = m_params.weightToi * (1.0f/(0.1f+tmin*m_invHorizTime));
+	
+	const float penalty = vpen + vcpen + spen + tpen;
+	
+	// Store different penalties for debug viewing
+	if (debug)
+		debug->addSample(vcand, cs, penalty, vpen, vcpen, spen, tpen);
+	
+	return penalty;
+}
+
+int dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax,
+												 const float* vel, const float* dvel, float* nvel,
+												 const dtObstacleAvoidanceParams* params,
+												 dtObstacleAvoidanceDebugData* debug)
+{
+	prepare(pos, dvel);
+	
+	memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams));
+	m_invHorizTime = 1.0f / m_params.horizTime;
+	m_vmax = vmax;
+	m_invVmax = 1.0f / vmax;
+	
+	dtVset(nvel, 0,0,0);
+	
+	if (debug)
+		debug->reset();
+
+	const float cvx = dvel[0] * m_params.velBias;
+	const float cvz = dvel[2] * m_params.velBias;
+	const float cs = vmax * 2 * (1 - m_params.velBias) / (float)(m_params.gridSize-1);
+	const float half = (m_params.gridSize-1)*cs*0.5f;
+		
+	float minPenalty = FLT_MAX;
+	int ns = 0;
+		
+	for (int y = 0; y < m_params.gridSize; ++y)
+	{
+		for (int x = 0; x < m_params.gridSize; ++x)
+		{
+			float vcand[3];
+			vcand[0] = cvx + x*cs - half;
+			vcand[1] = 0;
+			vcand[2] = cvz + y*cs - half;
+			
+			if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue;
+			
+			const float penalty = processSample(vcand, cs, pos,rad,vel,dvel, debug);
+			ns++;
+			if (penalty < minPenalty)
+			{
+				minPenalty = penalty;
+				dtVcopy(nvel, vcand);
+			}
+		}
+	}
+	
+	return ns;
+}
+
+
+int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
+													 const float* vel, const float* dvel, float* nvel,
+													 const dtObstacleAvoidanceParams* params,
+													 dtObstacleAvoidanceDebugData* debug)
+{
+	prepare(pos, dvel);
+	
+	memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams));
+	m_invHorizTime = 1.0f / m_params.horizTime;
+	m_vmax = vmax;
+	m_invVmax = 1.0f / vmax;
+	
+	dtVset(nvel, 0,0,0);
+	
+	if (debug)
+		debug->reset();
+
+	// Build sampling pattern aligned to desired velocity.
+	float pat[(DT_MAX_PATTERN_DIVS*DT_MAX_PATTERN_RINGS+1)*2];
+	int npat = 0;
+
+	const int ndivs = (int)m_params.adaptiveDivs;
+	const int nrings= (int)m_params.adaptiveRings;
+	const int depth = (int)m_params.adaptiveDepth;
+	
+	const int nd = dtClamp(ndivs, 1, DT_MAX_PATTERN_DIVS);
+	const int nr = dtClamp(nrings, 1, DT_MAX_PATTERN_RINGS);
+	const float da = (1.0f/nd) * DT_PI*2;
+	const float dang = dtMathAtan2f(dvel[2], dvel[0]);
+	
+	// Always add sample at zero
+	pat[npat*2+0] = 0;
+	pat[npat*2+1] = 0;
+	npat++;
+	
+	for (int j = 0; j < nr; ++j)
+	{
+		const float r = (float)(nr-j)/(float)nr;
+		float a = dang + (j&1)*0.5f*da;
+		for (int i = 0; i < nd; ++i)
+		{
+			pat[npat*2+0] = cosf(a)*r;
+			pat[npat*2+1] = sinf(a)*r;
+			npat++;
+			a += da;
+		}
+	}
+
+	// Start sampling.
+	float cr = vmax * (1.0f - m_params.velBias);
+	float res[3];
+	dtVset(res, dvel[0] * m_params.velBias, 0, dvel[2] * m_params.velBias);
+	int ns = 0;
+
+	for (int k = 0; k < depth; ++k)
+	{
+		float minPenalty = FLT_MAX;
+		float bvel[3];
+		dtVset(bvel, 0,0,0);
+		
+		for (int i = 0; i < npat; ++i)
+		{
+			float vcand[3];
+			vcand[0] = res[0] + pat[i*2+0]*cr;
+			vcand[1] = 0;
+			vcand[2] = res[2] + pat[i*2+1]*cr;
+			
+			if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue;
+			
+			const float penalty = processSample(vcand,cr/10, pos,rad,vel,dvel, debug);
+			ns++;
+			if (penalty < minPenalty)
+			{
+				minPenalty = penalty;
+				dtVcopy(bvel, vcand);
+			}
+		}
+
+		dtVcopy(res, bvel);
+
+		cr *= 0.5f;
+	}	
+	
+	dtVcopy(nvel, res);
+	
+	return ns;
+}
+

+ 597 - 0
Source/ThirdParty/DetourCrowd/source/DetourPathCorridor.cpp

@@ -0,0 +1,597 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <string.h>
+#include "DetourPathCorridor.h"
+#include "DetourNavMeshQuery.h"
+#include "DetourCommon.h"
+#include "DetourAssert.h"
+#include "DetourAlloc.h"
+
+
+int dtMergeCorridorStartMoved(dtPolyRef* path, const int npath, const int maxPath,
+							  const dtPolyRef* visited, const int nvisited)
+{
+	int furthestPath = -1;
+	int furthestVisited = -1;
+	
+	// Find furthest common polygon.
+	for (int i = npath-1; i >= 0; --i)
+	{
+		bool found = false;
+		for (int j = nvisited-1; j >= 0; --j)
+		{
+			if (path[i] == visited[j])
+			{
+				furthestPath = i;
+				furthestVisited = j;
+				found = true;
+			}
+		}
+		if (found)
+			break;
+	}
+	
+	// If no intersection found just return current path. 
+	if (furthestPath == -1 || furthestVisited == -1)
+		return npath;
+	
+	// Concatenate paths.	
+	
+	// Adjust beginning of the buffer to include the visited.
+	const int req = nvisited - furthestVisited;
+	const int orig = dtMin(furthestPath+1, npath);
+	int size = dtMax(0, npath-orig);
+	if (req+size > maxPath)
+		size = maxPath-req;
+	if (size)
+		memmove(path+req, path+orig, size*sizeof(dtPolyRef));
+	
+	// Store visited
+	for (int i = 0; i < req; ++i)
+		path[i] = visited[(nvisited-1)-i];				
+	
+	return req+size;
+}
+
+int dtMergeCorridorEndMoved(dtPolyRef* path, const int npath, const int maxPath,
+							const dtPolyRef* visited, const int nvisited)
+{
+	int furthestPath = -1;
+	int furthestVisited = -1;
+	
+	// Find furthest common polygon.
+	for (int i = 0; i < npath; ++i)
+	{
+		bool found = false;
+		for (int j = nvisited-1; j >= 0; --j)
+		{
+			if (path[i] == visited[j])
+			{
+				furthestPath = i;
+				furthestVisited = j;
+				found = true;
+			}
+		}
+		if (found)
+			break;
+	}
+	
+	// If no intersection found just return current path. 
+	if (furthestPath == -1 || furthestVisited == -1)
+		return npath;
+	
+	// Concatenate paths.
+	const int ppos = furthestPath+1;
+	const int vpos = furthestVisited+1;
+	const int count = dtMin(nvisited-vpos, maxPath-ppos);
+	dtAssert(ppos+count <= maxPath);
+	if (count)
+		memcpy(path+ppos, visited+vpos, sizeof(dtPolyRef)*count);
+	
+	return ppos+count;
+}
+
+int dtMergeCorridorStartShortcut(dtPolyRef* path, const int npath, const int maxPath,
+								 const dtPolyRef* visited, const int nvisited)
+{
+	int furthestPath = -1;
+	int furthestVisited = -1;
+	
+	// Find furthest common polygon.
+	for (int i = npath-1; i >= 0; --i)
+	{
+		bool found = false;
+		for (int j = nvisited-1; j >= 0; --j)
+		{
+			if (path[i] == visited[j])
+			{
+				furthestPath = i;
+				furthestVisited = j;
+				found = true;
+			}
+		}
+		if (found)
+			break;
+	}
+	
+	// If no intersection found just return current path. 
+	if (furthestPath == -1 || furthestVisited == -1)
+		return npath;
+	
+	// Concatenate paths.	
+	
+	// Adjust beginning of the buffer to include the visited.
+	const int req = furthestVisited;
+	if (req <= 0)
+		return npath;
+	
+	const int orig = furthestPath;
+	int size = dtMax(0, npath-orig);
+	if (req+size > maxPath)
+		size = maxPath-req;
+	if (size)
+		memmove(path+req, path+orig, size*sizeof(dtPolyRef));
+	
+	// Store visited
+	for (int i = 0; i < req; ++i)
+		path[i] = visited[i];
+	
+	return req+size;
+}
+
+/**
+@class dtPathCorridor
+@par
+
+The corridor is loaded with a path, usually obtained from a #dtNavMeshQuery::findPath() query. The corridor
+is then used to plan local movement, with the corridor automatically updating as needed to deal with inaccurate 
+agent locomotion.
+
+Example of a common use case:
+
+-# Construct the corridor object and call #init() to allocate its path buffer.
+-# Obtain a path from a #dtNavMeshQuery object.
+-# Use #reset() to set the agent's current position. (At the beginning of the path.)
+-# Use #setCorridor() to load the path and target.
+-# Use #findCorners() to plan movement. (This handles dynamic path straightening.)
+-# Use #movePosition() to feed agent movement back into the corridor. (The corridor will automatically adjust as needed.)
+-# If the target is moving, use #moveTargetPosition() to update the end of the corridor. 
+   (The corridor will automatically adjust as needed.)
+-# Repeat the previous 3 steps to continue to move the agent.
+
+The corridor position and target are always constrained to the navigation mesh.
+
+One of the difficulties in maintaining a path is that floating point errors, locomotion inaccuracies, and/or local 
+steering can result in the agent crossing the boundary of the path corridor, temporarily invalidating the path. 
+This class uses local mesh queries to detect and update the corridor as needed to handle these types of issues. 
+
+The fact that local mesh queries are used to move the position and target locations results in two beahviors that 
+need to be considered:
+
+Every time a move function is used there is a chance that the path will become non-optimial. Basically, the further 
+the target is moved from its original location, and the further the position is moved outside the original corridor, 
+the more likely the path will become non-optimal. This issue can be addressed by periodically running the 
+#optimizePathTopology() and #optimizePathVisibility() methods.
+
+All local mesh queries have distance limitations. (Review the #dtNavMeshQuery methods for details.) So the most accurate 
+use case is to move the position and target in small increments. If a large increment is used, then the corridor 
+may not be able to accurately find the new location.  Because of this limiation, if a position is moved in a large
+increment, then compare the desired and resulting polygon references. If the two do not match, then path replanning 
+may be needed.  E.g. If you move the target, check #getLastPoly() to see if it is the expected polygon.
+
+*/
+
+dtPathCorridor::dtPathCorridor() :
+	m_path(0),
+	m_npath(0),
+	m_maxPath(0)
+{
+}
+
+dtPathCorridor::~dtPathCorridor()
+{
+	dtFree(m_path);
+}
+
+/// @par
+///
+/// @warning Cannot be called more than once.
+bool dtPathCorridor::init(const int maxPath)
+{
+	dtAssert(!m_path);
+	m_path = (dtPolyRef*)dtAlloc(sizeof(dtPolyRef)*maxPath, DT_ALLOC_PERM);
+	if (!m_path)
+		return false;
+	m_npath = 0;
+	m_maxPath = maxPath;
+	return true;
+}
+
+/// @par
+///
+/// Essentially, the corridor is set of one polygon in size with the target
+/// equal to the position.
+void dtPathCorridor::reset(dtPolyRef ref, const float* pos)
+{
+	dtAssert(m_path);
+	dtVcopy(m_pos, pos);
+	dtVcopy(m_target, pos);
+	m_path[0] = ref;
+	m_npath = 1;
+}
+
+/**
+@par
+
+This is the function used to plan local movement within the corridor. One or more corners can be 
+detected in order to plan movement. It performs essentially the same function as #dtNavMeshQuery::findStraightPath.
+
+Due to internal optimizations, the maximum number of corners returned will be (@p maxCorners - 1) 
+For example: If the buffers are sized to hold 10 corners, the function will never return more than 9 corners. 
+So if 10 corners are needed, the buffers should be sized for 11 corners.
+
+If the target is within range, it will be the last corner and have a polygon reference id of zero.
+*/
+int dtPathCorridor::findCorners(float* cornerVerts, unsigned char* cornerFlags,
+							  dtPolyRef* cornerPolys, const int maxCorners,
+							  dtNavMeshQuery* navquery, const dtQueryFilter* /*filter*/)
+{
+	dtAssert(m_path);
+	dtAssert(m_npath);
+	
+	static const float MIN_TARGET_DIST = 0.01f;
+	
+	int ncorners = 0;
+	navquery->findStraightPath(m_pos, m_target, m_path, m_npath,
+							   cornerVerts, cornerFlags, cornerPolys, &ncorners, maxCorners);
+	
+	// Prune points in the beginning of the path which are too close.
+	while (ncorners)
+	{
+		if ((cornerFlags[0] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
+			dtVdist2DSqr(&cornerVerts[0], m_pos) > dtSqr(MIN_TARGET_DIST))
+			break;
+		ncorners--;
+		if (ncorners)
+		{
+			memmove(cornerFlags, cornerFlags+1, sizeof(unsigned char)*ncorners);
+			memmove(cornerPolys, cornerPolys+1, sizeof(dtPolyRef)*ncorners);
+			memmove(cornerVerts, cornerVerts+3, sizeof(float)*3*ncorners);
+		}
+	}
+	
+	// Prune points after an off-mesh connection.
+	for (int i = 0; i < ncorners; ++i)
+	{
+		if (cornerFlags[i] & DT_STRAIGHTPATH_OFFMESH_CONNECTION)
+		{
+			ncorners = i+1;
+			break;
+		}
+	}
+	
+	return ncorners;
+}
+
+/** 
+@par
+
+Inaccurate locomotion or dynamic obstacle avoidance can force the argent position significantly outside the 
+original corridor. Over time this can result in the formation of a non-optimal corridor. Non-optimal paths can 
+also form near the corners of tiles.
+
+This function uses an efficient local visibility search to try to optimize the corridor 
+between the current position and @p next.
+
+The corridor will change only if @p next is visible from the current position and moving directly toward the point 
+is better than following the existing path.
+
+The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency 
+of the call to match the needs to the agent.
+
+This function is not suitable for long distance searches.
+*/
+void dtPathCorridor::optimizePathVisibility(const float* next, const float pathOptimizationRange,
+										  dtNavMeshQuery* navquery, const dtQueryFilter* filter)
+{
+	dtAssert(m_path);
+	
+	// Clamp the ray to max distance.
+	float goal[3];
+	dtVcopy(goal, next);
+	float dist = dtVdist2D(m_pos, goal);
+	
+	// If too close to the goal, do not try to optimize.
+	if (dist < 0.01f)
+		return;
+	
+	// Overshoot a little. This helps to optimize open fields in tiled meshes.
+	dist = dtMin(dist+0.01f, pathOptimizationRange);
+	
+	// Adjust ray length.
+	float delta[3];
+	dtVsub(delta, goal, m_pos);
+	dtVmad(goal, m_pos, delta, pathOptimizationRange/dist);
+	
+	static const int MAX_RES = 32;
+	dtPolyRef res[MAX_RES];
+	float t, norm[3];
+	int nres = 0;
+	navquery->raycast(m_path[0], m_pos, goal, filter, &t, norm, res, &nres, MAX_RES);
+	if (nres > 1 && t > 0.99f)
+	{
+		m_npath = dtMergeCorridorStartShortcut(m_path, m_npath, m_maxPath, res, nres);
+	}
+}
+
+/**
+@par
+
+Inaccurate locomotion or dynamic obstacle avoidance can force the agent position significantly outside the 
+original corridor. Over time this can result in the formation of a non-optimal corridor. This function will use a 
+local area path search to try to re-optimize the corridor.
+
+The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency of 
+the call to match the needs to the agent.
+*/
+bool dtPathCorridor::optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
+{
+	dtAssert(navquery);
+	dtAssert(filter);
+	dtAssert(m_path);
+	
+	if (m_npath < 3)
+		return false;
+	
+	static const int MAX_ITER = 32;
+	static const int MAX_RES = 32;
+	
+	dtPolyRef res[MAX_RES];
+	int nres = 0;
+	navquery->initSlicedFindPath(m_path[0], m_path[m_npath-1], m_pos, m_target, filter);
+	navquery->updateSlicedFindPath(MAX_ITER, 0);
+	dtStatus status = navquery->finalizeSlicedFindPathPartial(m_path, m_npath, res, &nres, MAX_RES);
+	
+	if (dtStatusSucceed(status) && nres > 0)
+	{
+		m_npath = dtMergeCorridorStartShortcut(m_path, m_npath, m_maxPath, res, nres);
+		return true;
+	}
+	
+	return false;
+}
+
+bool dtPathCorridor::moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs,
+											   float* startPos, float* endPos,
+											   dtNavMeshQuery* navquery)
+{
+	dtAssert(navquery);
+	dtAssert(m_path);
+	dtAssert(m_npath);
+
+	// Advance the path up to and over the off-mesh connection.
+	dtPolyRef prevRef = 0, polyRef = m_path[0];
+	int npos = 0;
+	while (npos < m_npath && polyRef != offMeshConRef)
+	{
+		prevRef = polyRef;
+		polyRef = m_path[npos];
+		npos++;
+	}
+	if (npos == m_npath)
+	{
+		// Could not find offMeshConRef
+		return false;
+	}
+	
+	// Prune path
+	for (int i = npos; i < m_npath; ++i)
+		m_path[i-npos] = m_path[i];
+	m_npath -= npos;
+
+	refs[0] = prevRef;
+	refs[1] = polyRef;
+	
+	const dtNavMesh* nav = navquery->getAttachedNavMesh();
+	dtAssert(nav);
+
+	dtStatus status = nav->getOffMeshConnectionPolyEndPoints(refs[0], refs[1], startPos, endPos);
+	if (dtStatusSucceed(status))
+	{
+		dtVcopy(m_pos, endPos);
+		return true;
+	}
+
+	return false;
+}
+
+/**
+@par
+
+Behavior:
+
+- The movement is constrained to the surface of the navigation mesh. 
+- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid. 
+- The new position will be located in the adjusted corridor's first polygon.
+
+The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near' 
+depends on local polygon density, query search extents, etc.
+
+The resulting position will differ from the desired position if the desired position is not on the navigation mesh, 
+or it can't be reached using a local search.
+*/
+bool dtPathCorridor::movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
+{
+	dtAssert(m_path);
+	dtAssert(m_npath);
+	
+	// Move along navmesh and update new position.
+	float result[3];
+	static const int MAX_VISITED = 16;
+	dtPolyRef visited[MAX_VISITED];
+	int nvisited = 0;
+	dtStatus status = navquery->moveAlongSurface(m_path[0], m_pos, npos, filter,
+												 result, visited, &nvisited, MAX_VISITED);
+	if (dtStatusSucceed(status)) {
+		m_npath = dtMergeCorridorStartMoved(m_path, m_npath, m_maxPath, visited, nvisited);
+		
+		// Adjust the position to stay on top of the navmesh.
+		float h = m_pos[1];
+		navquery->getPolyHeight(m_path[0], result, &h);
+		result[1] = h;
+		dtVcopy(m_pos, result);
+		return true;
+	}
+	return false;
+}
+
+/**
+@par
+
+Behavior:
+
+- The movement is constrained to the surface of the navigation mesh. 
+- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid. 
+- The new target will be located in the adjusted corridor's last polygon.
+
+The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search extents, etc.
+
+The resulting target will differ from the desired target if the desired target is not on the navigation mesh, or it can't be reached using a local search.
+*/
+bool dtPathCorridor::moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
+{
+	dtAssert(m_path);
+	dtAssert(m_npath);
+	
+	// Move along navmesh and update new position.
+	float result[3];
+	static const int MAX_VISITED = 16;
+	dtPolyRef visited[MAX_VISITED];
+	int nvisited = 0;
+	dtStatus status = navquery->moveAlongSurface(m_path[m_npath-1], m_target, npos, filter,
+												 result, visited, &nvisited, MAX_VISITED);
+	if (dtStatusSucceed(status))
+	{
+		m_npath = dtMergeCorridorEndMoved(m_path, m_npath, m_maxPath, visited, nvisited);
+		// TODO: should we do that?
+		// Adjust the position to stay on top of the navmesh.
+		/*	float h = m_target[1];
+		 navquery->getPolyHeight(m_path[m_npath-1], result, &h);
+		 result[1] = h;*/
+		
+		dtVcopy(m_target, result);
+		
+		return true;
+	}
+	return false;
+}
+
+/// @par
+///
+/// The current corridor position is expected to be within the first polygon in the path. The target 
+/// is expected to be in the last polygon. 
+/// 
+/// @warning The size of the path must not exceed the size of corridor's path buffer set during #init().
+void dtPathCorridor::setCorridor(const float* target, const dtPolyRef* path, const int npath)
+{
+	dtAssert(m_path);
+	dtAssert(npath > 0);
+	dtAssert(npath < m_maxPath);
+	
+	dtVcopy(m_target, target);
+	memcpy(m_path, path, sizeof(dtPolyRef)*npath);
+	m_npath = npath;
+}
+
+bool dtPathCorridor::fixPathStart(dtPolyRef safeRef, const float* safePos)
+{
+	dtAssert(m_path);
+
+	dtVcopy(m_pos, safePos);
+	if (m_npath < 3 && m_npath > 0)
+	{
+		m_path[2] = m_path[m_npath-1];
+		m_path[0] = safeRef;
+		m_path[1] = 0;
+		m_npath = 3;
+	}
+	else
+	{
+		m_path[0] = safeRef;
+		m_path[1] = 0;
+	}
+	
+	return true;
+}
+
+bool dtPathCorridor::trimInvalidPath(dtPolyRef safeRef, const float* safePos,
+									 dtNavMeshQuery* navquery, const dtQueryFilter* filter)
+{
+	dtAssert(navquery);
+	dtAssert(filter);
+	dtAssert(m_path);
+	
+	// Keep valid path as far as possible.
+	int n = 0;
+	while (n < m_npath && navquery->isValidPolyRef(m_path[n], filter)) {
+		n++;
+	}
+	
+	if (n == m_npath)
+	{
+		// All valid, no need to fix.
+		return true;
+	}
+	else if (n == 0)
+	{
+		// The first polyref is bad, use current safe values.
+		dtVcopy(m_pos, safePos);
+		m_path[0] = safeRef;
+		m_npath = 1;
+	}
+	else
+	{
+		// The path is partially usable.
+		m_npath = n;
+	}
+	
+	// Clamp target pos to last poly
+	float tgt[3];
+	dtVcopy(tgt, m_target);
+	navquery->closestPointOnPolyBoundary(m_path[m_npath-1], tgt, m_target);
+	
+	return true;
+}
+
+/// @par
+///
+/// The path can be invalidated if there are structural changes to the underlying navigation mesh, or the state of 
+/// a polygon within the path changes resulting in it being filtered out. (E.g. An exclusion or inclusion flag changes.)
+bool dtPathCorridor::isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
+{
+	// Check that all polygons still pass query filter.
+	const int n = dtMin(m_npath, maxLookAhead);
+	for (int i = 0; i < n; ++i)
+	{
+		if (!navquery->isValidPolyRef(m_path[i], filter))
+			return false;
+	}
+
+	return true;
+}

+ 200 - 0
Source/ThirdParty/DetourCrowd/source/DetourPathQueue.cpp

@@ -0,0 +1,200 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <string.h>
+#include "DetourPathQueue.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+#include "DetourAlloc.h"
+#include "DetourCommon.h"
+
+
+dtPathQueue::dtPathQueue() :
+	m_nextHandle(1),
+	m_maxPathSize(0),
+	m_queueHead(0),
+	m_navquery(0)
+{
+	for (int i = 0; i < MAX_QUEUE; ++i)
+		m_queue[i].path = 0;
+}
+
+dtPathQueue::~dtPathQueue()
+{
+	purge();
+}
+
+void dtPathQueue::purge()
+{
+	dtFreeNavMeshQuery(m_navquery);
+	m_navquery = 0;
+	for (int i = 0; i < MAX_QUEUE; ++i)
+	{
+		dtFree(m_queue[i].path);
+		m_queue[i].path = 0;
+	}
+}
+
+bool dtPathQueue::init(const int maxPathSize, const int maxSearchNodeCount, dtNavMesh* nav)
+{
+	purge();
+
+	m_navquery = dtAllocNavMeshQuery();
+	if (!m_navquery)
+		return false;
+	if (dtStatusFailed(m_navquery->init(nav, maxSearchNodeCount)))
+		return false;
+	
+	m_maxPathSize = maxPathSize;
+	for (int i = 0; i < MAX_QUEUE; ++i)
+	{
+		m_queue[i].ref = DT_PATHQ_INVALID;
+		m_queue[i].path = (dtPolyRef*)dtAlloc(sizeof(dtPolyRef)*m_maxPathSize, DT_ALLOC_PERM);
+		if (!m_queue[i].path)
+			return false;
+	}
+	
+	m_queueHead = 0;
+	
+	return true;
+}
+
+void dtPathQueue::update(const int maxIters)
+{
+	static const int MAX_KEEP_ALIVE = 2; // in update ticks.
+
+	// Update path request until there is nothing to update
+	// or upto maxIters pathfinder iterations has been consumed.
+	int iterCount = maxIters;
+	
+	for (int i = 0; i < MAX_QUEUE; ++i)
+	{
+		PathQuery& q = m_queue[m_queueHead % MAX_QUEUE];
+		
+		// Skip inactive requests.
+		if (q.ref == DT_PATHQ_INVALID)
+		{
+			m_queueHead++;
+			continue;
+		}
+		
+		// Handle completed request.
+		if (dtStatusSucceed(q.status) || dtStatusFailed(q.status))
+		{
+			// If the path result has not been read in few frames, free the slot.
+			q.keepAlive++;
+			if (q.keepAlive > MAX_KEEP_ALIVE)
+			{
+				q.ref = DT_PATHQ_INVALID;
+				q.status = 0;
+			}
+			
+			m_queueHead++;
+			continue;
+		}
+		
+		// Handle query start.
+		if (q.status == 0)
+		{
+			q.status = m_navquery->initSlicedFindPath(q.startRef, q.endRef, q.startPos, q.endPos, q.filter);
+		}		
+		// Handle query in progress.
+		if (dtStatusInProgress(q.status))
+		{
+			int iters = 0;
+			q.status = m_navquery->updateSlicedFindPath(iterCount, &iters);
+			iterCount -= iters;
+		}
+		if (dtStatusSucceed(q.status))
+		{
+			q.status = m_navquery->finalizeSlicedFindPath(q.path, &q.npath, m_maxPathSize);
+		}
+
+		if (iterCount <= 0)
+			break;
+
+		m_queueHead++;
+	}
+}
+
+dtPathQueueRef dtPathQueue::request(dtPolyRef startRef, dtPolyRef endRef,
+									const float* startPos, const float* endPos,
+									const dtQueryFilter* filter)
+{
+	// Find empty slot
+	int slot = -1;
+	for (int i = 0; i < MAX_QUEUE; ++i)
+	{
+		if (m_queue[i].ref == DT_PATHQ_INVALID)
+		{
+			slot = i;
+			break;
+		}
+	}
+	// Could not find slot.
+	if (slot == -1)
+		return DT_PATHQ_INVALID;
+	
+	dtPathQueueRef ref = m_nextHandle++;
+	if (m_nextHandle == DT_PATHQ_INVALID) m_nextHandle++;
+	
+	PathQuery& q = m_queue[slot];
+	q.ref = ref;
+	dtVcopy(q.startPos, startPos);
+	q.startRef = startRef;
+	dtVcopy(q.endPos, endPos);
+	q.endRef = endRef;
+	
+	q.status = 0;
+	q.npath = 0;
+	q.filter = filter;
+	q.keepAlive = 0;
+	
+	return ref;
+}
+
+dtStatus dtPathQueue::getRequestStatus(dtPathQueueRef ref) const
+{
+	for (int i = 0; i < MAX_QUEUE; ++i)
+	{
+		if (m_queue[i].ref == ref)
+			return m_queue[i].status;
+	}
+	return DT_FAILURE;
+}
+
+dtStatus dtPathQueue::getPathResult(dtPathQueueRef ref, dtPolyRef* path, int* pathSize, const int maxPath)
+{
+	for (int i = 0; i < MAX_QUEUE; ++i)
+	{
+		if (m_queue[i].ref == ref)
+		{
+			PathQuery& q = m_queue[i];
+			dtStatus details = q.status & DT_STATUS_DETAIL_MASK;
+			// Free request for reuse.
+			q.ref = DT_PATHQ_INVALID;
+			q.status = 0;
+			// Copy path
+			int n = dtMin(q.npath, maxPath);
+			memcpy(path, q.path, sizeof(dtPolyRef)*n);
+			*pathSize = n;
+			return details | DT_SUCCESS;
+		}
+	}
+	return DT_FAILURE;
+}

+ 194 - 0
Source/ThirdParty/DetourCrowd/source/DetourProximityGrid.cpp

@@ -0,0 +1,194 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <string.h>
+#include <new>
+#include "DetourProximityGrid.h"
+#include "DetourCommon.h"
+#include "DetourMath.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+
+
+dtProximityGrid* dtAllocProximityGrid()
+{
+	void* mem = dtAlloc(sizeof(dtProximityGrid), DT_ALLOC_PERM);
+	if (!mem) return 0;
+	return new(mem) dtProximityGrid;
+}
+
+void dtFreeProximityGrid(dtProximityGrid* ptr)
+{
+	if (!ptr) return;
+	ptr->~dtProximityGrid();
+	dtFree(ptr);
+}
+
+
+inline int hashPos2(int x, int y, int n)
+{
+	return ((x*73856093) ^ (y*19349663)) & (n-1);
+}
+
+
+dtProximityGrid::dtProximityGrid() :
+	m_maxItems(0),
+	m_cellSize(0),
+	m_pool(0),
+	m_poolHead(0),
+	m_poolSize(0),
+	m_buckets(0),
+	m_bucketsSize(0)
+{
+}
+
+dtProximityGrid::~dtProximityGrid()
+{
+	dtFree(m_buckets);
+	dtFree(m_pool);
+}
+
+bool dtProximityGrid::init(const int poolSize, const float cellSize)
+{
+	dtAssert(poolSize > 0);
+	dtAssert(cellSize > 0.0f);
+	
+	m_cellSize = cellSize;
+	m_invCellSize = 1.0f / m_cellSize;
+	
+	// Allocate hashs buckets
+	m_bucketsSize = dtNextPow2(poolSize);
+	m_buckets = (unsigned short*)dtAlloc(sizeof(unsigned short)*m_bucketsSize, DT_ALLOC_PERM);
+	if (!m_buckets)
+		return false;
+	
+	// Allocate pool of items.
+	m_poolSize = poolSize;
+	m_poolHead = 0;
+	m_pool = (Item*)dtAlloc(sizeof(Item)*m_poolSize, DT_ALLOC_PERM);
+	if (!m_pool)
+		return false;
+	
+	clear();
+	
+	return true;
+}
+
+void dtProximityGrid::clear()
+{
+	memset(m_buckets, 0xff, sizeof(unsigned short)*m_bucketsSize);
+	m_poolHead = 0;
+	m_bounds[0] = 0xffff;
+	m_bounds[1] = 0xffff;
+	m_bounds[2] = -0xffff;
+	m_bounds[3] = -0xffff;
+}
+
+void dtProximityGrid::addItem(const unsigned short id,
+							  const float minx, const float miny,
+							  const float maxx, const float maxy)
+{
+	const int iminx = (int)dtMathFloorf(minx * m_invCellSize);
+	const int iminy = (int)dtMathFloorf(miny * m_invCellSize);
+	const int imaxx = (int)dtMathFloorf(maxx * m_invCellSize);
+	const int imaxy = (int)dtMathFloorf(maxy * m_invCellSize);
+	
+	m_bounds[0] = dtMin(m_bounds[0], iminx);
+	m_bounds[1] = dtMin(m_bounds[1], iminy);
+	m_bounds[2] = dtMax(m_bounds[2], imaxx);
+	m_bounds[3] = dtMax(m_bounds[3], imaxy);
+	
+	for (int y = iminy; y <= imaxy; ++y)
+	{
+		for (int x = iminx; x <= imaxx; ++x)
+		{
+			if (m_poolHead < m_poolSize)
+			{
+				const int h = hashPos2(x, y, m_bucketsSize);
+				const unsigned short idx = (unsigned short)m_poolHead;
+				m_poolHead++;
+				Item& item = m_pool[idx];
+				item.x = (short)x;
+				item.y = (short)y;
+				item.id = id;
+				item.next = m_buckets[h];
+				m_buckets[h] = idx;
+			}
+		}
+	}
+}
+
+int dtProximityGrid::queryItems(const float minx, const float miny,
+								const float maxx, const float maxy,
+								unsigned short* ids, const int maxIds) const
+{
+	const int iminx = (int)dtMathFloorf(minx * m_invCellSize);
+	const int iminy = (int)dtMathFloorf(miny * m_invCellSize);
+	const int imaxx = (int)dtMathFloorf(maxx * m_invCellSize);
+	const int imaxy = (int)dtMathFloorf(maxy * m_invCellSize);
+	
+	int n = 0;
+	
+	for (int y = iminy; y <= imaxy; ++y)
+	{
+		for (int x = iminx; x <= imaxx; ++x)
+		{
+			const int h = hashPos2(x, y, m_bucketsSize);
+			unsigned short idx = m_buckets[h];
+			while (idx != 0xffff)
+			{
+				Item& item = m_pool[idx];
+				if ((int)item.x == x && (int)item.y == y)
+				{
+					// Check if the id exists already.
+					const unsigned short* end = ids + n;
+					unsigned short* i = ids;
+					while (i != end && *i != item.id)
+						++i;
+					// Item not found, add it.
+					if (i == end)
+					{
+						if (n >= maxIds)
+							return n;
+						ids[n++] = item.id;
+					}
+				}
+				idx = item.next;
+			}
+		}
+	}
+	
+	return n;
+}
+
+int dtProximityGrid::getItemCountAt(const int x, const int y) const
+{
+	int n = 0;
+	
+	const int h = hashPos2(x, y, m_bucketsSize);
+	unsigned short idx = m_buckets[h];
+	while (idx != 0xffff)
+	{
+		Item& item = m_pool[idx];
+		if ((int)item.x == x && (int)item.y == y)
+			n++;
+		idx = item.next;
+	}
+	
+	return n;
+}

+ 36 - 0
Source/ThirdParty/DetourTileCache/CMakeLists.txt

@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2008-2015 the Urho3D project.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+# Define target name
+set (TARGET_NAME DetourTileCache)
+
+# Define source files
+define_source_files (GLOB_CPP_PATTERNS source/*.cpp GLOB_H_PATTERNS include/*.h)
+
+# Define dependency libs
+set (INCLUDE_DIRS include ../Detour/include)
+
+# Setup target
+setup_library ()
+
+# Install headers for building the Urho3D library
+install_header_files (DIRECTORY include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/DetourTileCache FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant

+ 18 - 0
Source/ThirdParty/DetourTileCache/License.txt

@@ -0,0 +1,18 @@
+Copyright (c) 2009 Mikko Mononen [email protected]
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+

+ 62 - 0
Source/ThirdParty/DetourTileCache/README.md

@@ -0,0 +1,62 @@
+
+Recast & Detour
+===============
+
+[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/memononen/recastnavigation/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
+
+![screenshot of a navmesh baked with the sample program](/RecastDemo/screenshot.png?raw=true)
+
+## Recast
+
+Recast is state of the art navigation mesh construction toolset for games.
+
+* It is automatic, which means that you can throw any level geometry at it and you will get robust mesh out
+* It is fast which means swift turnaround times for level designers
+* It is open source so it comes with full source and you can customize it to your heart's content. 
+
+The Recast process starts with constructing a voxel mold from a level geometry 
+and then casting a navigation mesh over it. The process consists of three steps, 
+building the voxel mold, partitioning the mold into simple regions, peeling off 
+the regions as simple polygons.
+
+1. The voxel mold is build from the input triangle mesh by rasterizing the triangles into a multi-layer heightfield. Some simple filters are  then applied to the mold to prune out locations where the character would not be able to move.
+2. The walkable areas described by the mold are divided into simple overlayed 2D regions. The resulting regions have only one non-overlapping contour, which simplifies the final step of the process tremendously.
+3. The navigation polygons are peeled off from the regions by first tracing the boundaries and then simplifying them. The resulting polygons are finally converted to convex polygons which makes them perfect for pathfinding and spatial reasoning about the level. 
+
+
+## Detour
+
+Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
+
+Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows you to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes. 
+
+
+## Recast Demo
+
+You can find a comprehensive demo project in RecastDemo folder. It is a kitchen sink demo containing all the functionality of the library. If you are new to Recast & Detour, check out [Sample_SoloMesh.cpp](/RecastDemo/Source/Sample_SoloMesh.cpp) to get started with building navmeshes and [NavMeshTesterTool.cpp](/RecastDemo/Source/NavMeshTesterTool.cpp) to see how Detour can be used to find paths.
+
+### Building RecastDemo
+
+RecastDemo uses [premake4](http://industriousone.com/premake) to build platform specific projects, now is good time to install it if you don't have it already. To build *RecasDemo*, in your favorite terminal navigate into the `RecastDemo` folder, then:
+
+- *OS X*: `premake4 xcode4`
+- *Windows*: `premake4 vs2010`
+- *Linux*: `premake4 gmake`
+
+See premake4 documentation for full list of supported build file types. The projects will be created in `RecastDemo/Build` folder. And after you have compiled the project, the *RecastDemo* executable will be located in `RecastDemo/Bin` folder.
+
+
+## Integrating with your own project
+
+It is recommended to add the source directories `DebugUtils`, `Detour`, `DetourCrowd`, `DetourTileCache`, and `Recast` into your own project depending on which parts of the project you need. For example your level building tool could include DebugUtils, Recast, and Detour, and your game runtime could just include Detour.
+
+
+## Discuss
+
+- Discuss Recast & Detour: http://groups.google.com/group/recastnavigation
+- Development blog: http://digestingduck.blogspot.com/
+
+
+## License
+
+Recast & Detour is licensed under ZLib license, see License.txt for more information.

+ 120 - 0
Source/ThirdParty/DetourTileCache/Readme.txt

@@ -0,0 +1,120 @@
+
+Recast & Detour Version 1.4
+
+
+Recast
+
+Recast is state of the art navigation mesh construction toolset for games.
+
+    * It is automatic, which means that you can throw any level geometry
+      at it and you will get robust mesh out
+    * It is fast which means swift turnaround times for level designers
+    * It is open source so it comes with full source and you can
+      customize it to your hearts content. 
+
+The Recast process starts with constructing a voxel mold from a level geometry 
+and then casting a navigation mesh over it. The process consists of three steps, 
+building the voxel mold, partitioning the mold into simple regions, peeling off 
+the regions as simple polygons.
+
+   1. The voxel mold is build from the input triangle mesh by rasterizing 
+      the triangles into a multi-layer heightfield. Some simple filters are 
+      then applied to the mold to prune out locations where the character 
+      would not be able to move.
+   2. The walkable areas described by the mold are divided into simple 
+      overlayed 2D regions. The resulting regions have only one non-overlapping 
+      contour, which simplifies the final step of the process tremendously.
+   3. The navigation polygons are peeled off from the regions by first tracing 
+      the boundaries and then simplifying them. The resulting polygons are 
+      finally converted to convex polygons which makes them perfect for 
+      pathfinding and spatial reasoning about the level. 
+
+The toolset code is located in the Recast folder and demo application using the Recast
+toolset is located in the RecastDemo folder.
+
+The project files with this distribution can be compiled with Microsoft Visual C++ 2008
+(you can download it for free) and XCode 3.1.
+
+
+Detour
+
+Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
+
+Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes. 
+
+
+Latest code available at http://code.google.com/p/recastnavigation/
+
+
+--
+
+Release Notes
+
+----------------
+* Recast 1.4
+  Released August 24th, 2009
+
+- Added detail height mesh generation (RecastDetailMesh.cpp) for single,
+  tiled statmeshes as well as tilemesh.
+- Added feature to contour tracing which detects extra vertices along
+  tile edges which should be removed later.
+- Changed the tiled stat mesh preprocess, so that it first generated
+  polymeshes per tile and finally combines them.
+- Fixed bug in the GUI code where invisible buttons could be pressed.
+
+----------------
+* Recast 1.31
+  Released July 24th, 2009
+
+- Better cost and heuristic functions.
+- Fixed tile navmesh raycast on tile borders.
+
+----------------
+* Recast 1.3
+  Released July 14th, 2009
+
+- Added dtTileNavMesh which allows to dynamically add and remove navmesh pieces at runtime.
+- Renamed stat navmesh types to dtStat* (i.e. dtPoly is now dtStatPoly).
+- Moved common code used by tile and stat navmesh to DetourNode.h/cpp and DetourCommon.h/cpp.
+- Refactores the demo code.
+
+----------------
+* Recast 1.2
+  Released June 17th, 2009
+
+- Added tiled mesh generation. The tiled generation allows to generate navigation for
+  much larger worlds, it removes some of the artifacts that comes from distance fields
+  in open areas, and allows later streaming and dynamic runtime generation
+- Improved and added some debug draw modes
+- API change: The helper function rcBuildNavMesh does not exists anymore,
+  had to change few internal things to cope with the tiled processing,
+  similar API functionality will be added later once the tiled process matures
+- The demo is getting way too complicated, need to split demos
+- Fixed several filtering functions so that the mesh is tighter to the geometry,
+  sometimes there could be up error up to tow voxel units close to walls,
+  now it should be just one.
+
+----------------
+* Recast 1.1
+  Released April 11th, 2009
+
+This is the first release of Detour.
+
+----------------
+* Recast 1.0
+  Released March 29th, 2009
+
+This is the first release of Recast.
+
+The process is not always as robust as I would wish. The watershed phase sometimes swallows tiny islands
+which are close to edges. These droppings are handled in rcBuildContours, but the code is not
+particularly robust either.
+
+Another non-robust case is when portal contours (contours shared between two regions) are always
+assumed to be straight. That can lead to overlapping contours specially when the level has
+large open areas.
+
+
+
+Mikko Mononen
[email protected]

+ 20 - 0
Source/ThirdParty/DetourTileCache/TODO.txt

@@ -0,0 +1,20 @@
+TODO/Roadmap
+
+Summer/Autumn 2009
+
+- Off mesh links (jump links)
+- Area annotations
+- Embed extra data per polygon
+- Height conforming navmesh
+
+
+Autumn/Winter 2009/2010
+
+- Detour path following
+- More dynamic example with tile navmesh
+- Faster small tile process
+
+
+More info at http://digestingduck.blogspot.com/2009/07/recast-and-detour-roadmap.html
+
+-

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

@@ -0,0 +1,212 @@
+#ifndef DETOURTILECACHE_H
+#define DETOURTILECACHE_H
+
+#include "DetourStatus.h"
+
+
+
+typedef unsigned int dtObstacleRef;
+
+typedef unsigned int dtCompressedTileRef;
+
+/// Flags for addTile
+enum dtCompressedTileFlags
+{
+	DT_COMPRESSEDTILE_FREE_DATA = 0x01,					///< Navmesh owns the tile memory and should free it.
+};
+
+struct dtCompressedTile
+{
+	unsigned int salt;						///< Counter describing modifications to the tile.
+	struct dtTileCacheLayerHeader* header;
+	unsigned char* compressed;
+	int compressedSize;
+	unsigned char* data;
+	int dataSize;
+	unsigned int flags;
+	dtCompressedTile* next;
+};
+
+enum ObstacleState
+{
+	DT_OBSTACLE_EMPTY,
+	DT_OBSTACLE_PROCESSING,
+	DT_OBSTACLE_PROCESSED,
+	DT_OBSTACLE_REMOVING,
+};
+
+static const int DT_MAX_TOUCHED_TILES = 8;
+struct dtTileCacheObstacle
+{
+	float pos[3], radius, height;
+	dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES];
+	dtCompressedTileRef pending[DT_MAX_TOUCHED_TILES];
+	unsigned short salt;
+	unsigned char state;
+	unsigned char ntouched;
+	unsigned char npending;
+	dtTileCacheObstacle* next;
+};
+
+struct dtTileCacheParams
+{
+	float orig[3];
+	float cs, ch;
+	int width, height;
+	float walkableHeight;
+	float walkableRadius;
+	float walkableClimb;
+	float maxSimplificationError;
+	int maxTiles;
+	int maxObstacles;
+};
+
+struct dtTileCacheMeshProcess
+{
+	virtual ~dtTileCacheMeshProcess() { }
+
+	virtual void process(struct dtNavMeshCreateParams* params,
+						 unsigned char* polyAreas, unsigned short* polyFlags) = 0;
+};
+
+
+class dtTileCache
+{
+public:
+	dtTileCache();
+	~dtTileCache();
+	
+	struct dtTileCacheAlloc* getAlloc() { return m_talloc; }
+	struct dtTileCacheCompressor* getCompressor() { return m_tcomp; }
+	const dtTileCacheParams* getParams() const { return &m_params; }
+	
+	inline int getTileCount() const { return m_params.maxTiles; }
+	inline const dtCompressedTile* getTile(const int i) const { return &m_tiles[i]; }
+	
+	inline int getObstacleCount() const { return m_params.maxObstacles; }
+	inline const dtTileCacheObstacle* getObstacle(const int i) const { return &m_obstacles[i]; }
+	
+	const dtTileCacheObstacle* getObstacleByRef(dtObstacleRef ref);
+	
+	dtObstacleRef getObstacleRef(const dtTileCacheObstacle* obmin) const;
+	
+	dtStatus init(const dtTileCacheParams* params,
+				  struct dtTileCacheAlloc* talloc,
+				  struct dtTileCacheCompressor* tcomp,
+				  struct dtTileCacheMeshProcess* tmproc);
+	
+	int getTilesAt(const int tx, const int ty, dtCompressedTileRef* tiles, const int maxTiles) const ;
+	
+	dtCompressedTile* getTileAt(const int tx, const int ty, const int tlayer);
+	dtCompressedTileRef getTileRef(const dtCompressedTile* tile) const;
+	const dtCompressedTile* getTileByRef(dtCompressedTileRef ref) const;
+	
+	dtStatus addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result);
+	
+	dtStatus removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize);
+	
+	dtStatus addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result);
+	dtStatus removeObstacle(const dtObstacleRef ref);
+	
+	dtStatus queryTiles(const float* bmin, const float* bmax,
+						dtCompressedTileRef* results, int* resultCount, const int maxResults) const;
+	
+	dtStatus update(const float /*dt*/, class dtNavMesh* navmesh);
+	
+	dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh);
+	
+	dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh);
+	
+	void calcTightTileBounds(const struct dtTileCacheLayerHeader* header, float* bmin, float* bmax) const;
+	
+	void getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const;
+	
+
+	/// Encodes a tile id.
+	inline dtCompressedTileRef encodeTileId(unsigned int salt, unsigned int it) const
+	{
+		return ((dtCompressedTileRef)salt << m_tileBits) | (dtCompressedTileRef)it;
+	}
+	
+	/// Decodes a tile salt.
+	inline unsigned int decodeTileIdSalt(dtCompressedTileRef ref) const
+	{
+		const dtCompressedTileRef saltMask = ((dtCompressedTileRef)1<<m_saltBits)-1;
+		return (unsigned int)((ref >> m_tileBits) & saltMask);
+	}
+	
+	/// Decodes a tile id.
+	inline unsigned int decodeTileIdTile(dtCompressedTileRef ref) const
+	{
+		const dtCompressedTileRef tileMask = ((dtCompressedTileRef)1<<m_tileBits)-1;
+		return (unsigned int)(ref & tileMask);
+	}
+
+	/// Encodes an obstacle id.
+	inline dtObstacleRef encodeObstacleId(unsigned int salt, unsigned int it) const
+	{
+		return ((dtObstacleRef)salt << 16) | (dtObstacleRef)it;
+	}
+	
+	/// Decodes an obstacle salt.
+	inline unsigned int decodeObstacleIdSalt(dtObstacleRef ref) const
+	{
+		const dtObstacleRef saltMask = ((dtObstacleRef)1<<16)-1;
+		return (unsigned int)((ref >> 16) & saltMask);
+	}
+	
+	/// Decodes an obstacle id.
+	inline unsigned int decodeObstacleIdObstacle(dtObstacleRef ref) const
+	{
+		const dtObstacleRef tileMask = ((dtObstacleRef)1<<16)-1;
+		return (unsigned int)(ref & tileMask);
+	}
+	
+	
+private:
+	
+	enum ObstacleRequestAction
+	{
+		REQUEST_ADD,
+		REQUEST_REMOVE,
+	};
+	
+	struct ObstacleRequest
+	{
+		int action;
+		dtObstacleRef ref;
+	};
+	
+	int m_tileLutSize;						///< Tile hash lookup size (must be pot).
+	int m_tileLutMask;						///< Tile hash lookup mask.
+	
+	dtCompressedTile** m_posLookup;			///< Tile hash lookup.
+	dtCompressedTile* m_nextFreeTile;		///< Freelist of tiles.
+	dtCompressedTile* m_tiles;				///< List of tiles.
+	
+	unsigned int m_saltBits;				///< Number of salt bits in the tile ID.
+	unsigned int m_tileBits;				///< Number of tile bits in the tile ID.
+	
+	dtTileCacheParams m_params;
+	
+	dtTileCacheAlloc* m_talloc;
+	dtTileCacheCompressor* m_tcomp;
+	dtTileCacheMeshProcess* m_tmproc;
+	
+	dtTileCacheObstacle* m_obstacles;
+	dtTileCacheObstacle* m_nextFreeObstacle;
+	
+	static const int MAX_REQUESTS = 64;
+	ObstacleRequest m_reqs[MAX_REQUESTS];
+	int m_nreqs;
+	
+	static const int MAX_UPDATE = 64;
+	dtCompressedTileRef m_update[MAX_UPDATE];
+	int m_nupdate;
+	
+};
+
+dtTileCache* dtAllocTileCache();
+void dtFreeTileCache(dtTileCache* tc);
+
+#endif

+ 152 - 0
Source/ThirdParty/DetourTileCache/include/DetourTileCacheBuilder.h

@@ -0,0 +1,152 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURTILECACHEBUILDER_H
+#define DETOURTILECACHEBUILDER_H
+
+#include "DetourAlloc.h"
+#include "DetourStatus.h"
+
+static const int DT_TILECACHE_MAGIC = 'D'<<24 | 'T'<<16 | 'L'<<8 | 'R'; ///< 'DTLR';
+static const int DT_TILECACHE_VERSION = 1;
+
+static const unsigned char DT_TILECACHE_NULL_AREA = 0;
+static const unsigned char DT_TILECACHE_WALKABLE_AREA = 63;
+static const unsigned short DT_TILECACHE_NULL_IDX = 0xffff;
+
+struct dtTileCacheLayerHeader
+{
+	int magic;								///< Data magic
+	int version;							///< Data version
+	int tx,ty,tlayer;
+	float bmin[3], bmax[3];
+	unsigned short hmin, hmax;				///< Height min/max range
+	unsigned char width, height;			///< Dimension of the layer.
+	unsigned char minx, maxx, miny, maxy;	///< Usable sub-region.
+};
+
+struct dtTileCacheLayer
+{
+	dtTileCacheLayerHeader* header;
+	unsigned char regCount;					///< Region count.
+	unsigned char* heights;
+	unsigned char* areas;
+	unsigned char* cons;
+	unsigned char* regs;
+};
+
+struct dtTileCacheContour
+{
+	int nverts;
+	unsigned char* verts;
+	unsigned char reg;
+	unsigned char area;
+};
+
+struct dtTileCacheContourSet
+{
+	int nconts;
+	dtTileCacheContour* conts;
+};
+
+struct dtTileCachePolyMesh
+{
+	int nvp;
+	int nverts;				///< Number of vertices.
+	int npolys;				///< Number of polygons.
+	unsigned short* verts;	///< Vertices of the mesh, 3 elements per vertex.
+	unsigned short* polys;	///< Polygons of the mesh, nvp*2 elements per polygon.
+	unsigned short* flags;	///< Per polygon flags.
+	unsigned char* areas;	///< Area ID of polygons.
+};
+
+
+struct dtTileCacheAlloc
+{
+	virtual ~dtTileCacheAlloc() { }
+
+	virtual void reset()
+	{
+	}
+	
+	virtual void* alloc(const int size)
+	{
+		return dtAlloc(size, DT_ALLOC_TEMP);
+	}
+	
+	virtual void free(void* ptr)
+	{
+		dtFree(ptr);
+	}
+};
+
+struct dtTileCacheCompressor
+{
+	virtual ~dtTileCacheCompressor() { }
+
+	virtual int maxCompressedSize(const int bufferSize) = 0;
+	virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
+							  unsigned char* compressed, const int maxCompressedSize, int* compressedSize) = 0;
+	virtual dtStatus decompress(const unsigned char* compressed, const int compressedSize,
+								unsigned char* buffer, const int maxBufferSize, int* bufferSize) = 0;
+};
+
+
+dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
+							   dtTileCacheLayerHeader* header,
+							   const unsigned char* heights,
+							   const unsigned char* areas,
+							   const unsigned char* cons,
+							   unsigned char** outData, int* outDataSize);
+
+void dtFreeTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheLayer* layer);
+
+dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompressor* comp,
+									unsigned char* compressed, const int compressedSize,
+									dtTileCacheLayer** layerOut);
+
+dtTileCacheContourSet* dtAllocTileCacheContourSet(dtTileCacheAlloc* alloc);
+void dtFreeTileCacheContourSet(dtTileCacheAlloc* alloc, dtTileCacheContourSet* cset);
+
+dtTileCachePolyMesh* dtAllocTileCachePolyMesh(dtTileCacheAlloc* alloc);
+void dtFreeTileCachePolyMesh(dtTileCacheAlloc* alloc, dtTileCachePolyMesh* lmesh);
+
+dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
+							const float* pos, const float radius, const float height, const unsigned char areaId);
+
+dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc,
+								 dtTileCacheLayer& layer,
+								 const int walkableClimb);
+
+dtStatus dtBuildTileCacheContours(dtTileCacheAlloc* alloc,
+								  dtTileCacheLayer& layer,
+								  const int walkableClimb, 	const float maxError,
+								  dtTileCacheContourSet& lcset);
+
+dtStatus dtBuildTileCachePolyMesh(dtTileCacheAlloc* alloc,
+								  dtTileCacheContourSet& lcset,
+								  dtTileCachePolyMesh& mesh);
+
+/// Swaps the endianess of the compressed tile data's header (#dtTileCacheLayerHeader).
+/// Tile layer data does not need endian swapping as it consits only of bytes.
+///  @param[in,out]	data		The tile data array.
+///  @param[in]		dataSize	The size of the data array.
+bool dtTileCacheHeaderSwapEndian(unsigned char* data, const int dataSize);
+
+
+#endif // DETOURTILECACHEBUILDER_H

+ 704 - 0
Source/ThirdParty/DetourTileCache/source/DetourTileCache.cpp

@@ -0,0 +1,704 @@
+#include "DetourTileCache.h"
+#include "DetourTileCacheBuilder.h"
+#include "DetourNavMeshBuilder.h"
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+#include "DetourMath.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <string.h>
+#include <new>
+
+dtTileCache* dtAllocTileCache()
+{
+	void* mem = dtAlloc(sizeof(dtTileCache), DT_ALLOC_PERM);
+	if (!mem) return 0;
+	return new(mem) dtTileCache;
+}
+
+void dtFreeTileCache(dtTileCache* tc)
+{
+	if (!tc) return;
+	tc->~dtTileCache();
+	dtFree(tc);
+}
+
+static bool contains(const dtCompressedTileRef* a, const int n, const dtCompressedTileRef v)
+{
+	for (int i = 0; i < n; ++i)
+		if (a[i] == v)
+			return true;
+	return false;
+}
+
+inline int computeTileHash(int x, int y, const int mask)
+{
+	const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
+	const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
+	unsigned int n = h1 * x + h2 * y;
+	return (int)(n & mask);
+}
+
+
+struct BuildContext
+{
+	inline BuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {}
+	inline ~BuildContext() { purge(); }
+	void purge()
+	{
+		dtFreeTileCacheLayer(alloc, layer);
+		layer = 0;
+		dtFreeTileCacheContourSet(alloc, lcset);
+		lcset = 0;
+		dtFreeTileCachePolyMesh(alloc, lmesh);
+		lmesh = 0;
+	}
+	struct dtTileCacheLayer* layer;
+	struct dtTileCacheContourSet* lcset;
+	struct dtTileCachePolyMesh* lmesh;
+	struct dtTileCacheAlloc* alloc;
+};
+
+
+dtTileCache::dtTileCache() :
+	m_tileLutSize(0),
+	m_tileLutMask(0),
+	m_posLookup(0),
+	m_nextFreeTile(0),	
+	m_tiles(0),	
+	m_saltBits(0),
+	m_tileBits(0),
+	m_talloc(0),
+	m_tcomp(0),
+	m_tmproc(0),
+	m_obstacles(0),
+	m_nextFreeObstacle(0),
+	m_nreqs(0),
+	m_nupdate(0)
+{
+	memset(&m_params, 0, sizeof(m_params));
+}
+	
+dtTileCache::~dtTileCache()
+{
+	for (int i = 0; i < m_params.maxTiles; ++i)
+	{
+		if (m_tiles[i].flags & DT_COMPRESSEDTILE_FREE_DATA)
+		{
+			dtFree(m_tiles[i].data);
+			m_tiles[i].data = 0;
+		}
+	}
+	dtFree(m_obstacles);
+	m_obstacles = 0;
+	dtFree(m_posLookup);
+	m_posLookup = 0;
+	dtFree(m_tiles);
+	m_tiles = 0;
+	m_nreqs = 0;
+	m_nupdate = 0;
+}
+
+const dtCompressedTile* dtTileCache::getTileByRef(dtCompressedTileRef ref) const
+{
+	if (!ref)
+		return 0;
+	unsigned int tileIndex = decodeTileIdTile(ref);
+	unsigned int tileSalt = decodeTileIdSalt(ref);
+	if ((int)tileIndex >= m_params.maxTiles)
+		return 0;
+	const dtCompressedTile* tile = &m_tiles[tileIndex];
+	if (tile->salt != tileSalt)
+		return 0;
+	return tile;
+}
+
+
+dtStatus dtTileCache::init(const dtTileCacheParams* params,
+						   dtTileCacheAlloc* talloc,
+						   dtTileCacheCompressor* tcomp,
+						   dtTileCacheMeshProcess* tmproc)
+{
+	m_talloc = talloc;
+	m_tcomp = tcomp;
+	m_tmproc = tmproc;
+	m_nreqs = 0;
+	memcpy(&m_params, params, sizeof(m_params));
+	
+	// Alloc space for obstacles.
+	m_obstacles = (dtTileCacheObstacle*)dtAlloc(sizeof(dtTileCacheObstacle)*m_params.maxObstacles, DT_ALLOC_PERM);
+	if (!m_obstacles)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memset(m_obstacles, 0, sizeof(dtTileCacheObstacle)*m_params.maxObstacles);
+	m_nextFreeObstacle = 0;
+	for (int i = m_params.maxObstacles-1; i >= 0; --i)
+	{
+		m_obstacles[i].salt = 1;
+		m_obstacles[i].next = m_nextFreeObstacle;
+		m_nextFreeObstacle = &m_obstacles[i];
+	}
+	
+	// Init tiles
+	m_tileLutSize = dtNextPow2(m_params.maxTiles/4);
+	if (!m_tileLutSize) m_tileLutSize = 1;
+	m_tileLutMask = m_tileLutSize-1;
+	
+	m_tiles = (dtCompressedTile*)dtAlloc(sizeof(dtCompressedTile)*m_params.maxTiles, DT_ALLOC_PERM);
+	if (!m_tiles)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	m_posLookup = (dtCompressedTile**)dtAlloc(sizeof(dtCompressedTile*)*m_tileLutSize, DT_ALLOC_PERM);
+	if (!m_posLookup)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memset(m_tiles, 0, sizeof(dtCompressedTile)*m_params.maxTiles);
+	memset(m_posLookup, 0, sizeof(dtCompressedTile*)*m_tileLutSize);
+	m_nextFreeTile = 0;
+	for (int i = m_params.maxTiles-1; i >= 0; --i)
+	{
+		m_tiles[i].salt = 1;
+		m_tiles[i].next = m_nextFreeTile;
+		m_nextFreeTile = &m_tiles[i];
+	}
+	
+	// Init ID generator values.
+	m_tileBits = dtIlog2(dtNextPow2((unsigned int)m_params.maxTiles));
+	// Only allow 31 salt bits, since the salt mask is calculated using 32bit uint and it will overflow.
+	m_saltBits = dtMin((unsigned int)31, 32 - m_tileBits);
+	if (m_saltBits < 10)
+		return DT_FAILURE | DT_INVALID_PARAM;
+	
+	return DT_SUCCESS;
+}
+
+int dtTileCache::getTilesAt(const int tx, const int ty, dtCompressedTileRef* tiles, const int maxTiles) const 
+{
+	int n = 0;
+	
+	// Find tile based on hash.
+	int h = computeTileHash(tx,ty,m_tileLutMask);
+	dtCompressedTile* tile = m_posLookup[h];
+	while (tile)
+	{
+		if (tile->header &&
+			tile->header->tx == tx &&
+			tile->header->ty == ty)
+		{
+			if (n < maxTiles)
+				tiles[n++] = getTileRef(tile);
+		}
+		tile = tile->next;
+	}
+	
+	return n;
+}
+
+dtCompressedTile* dtTileCache::getTileAt(const int tx, const int ty, const int tlayer)
+{
+	// Find tile based on hash.
+	int h = computeTileHash(tx,ty,m_tileLutMask);
+	dtCompressedTile* tile = m_posLookup[h];
+	while (tile)
+	{
+		if (tile->header &&
+			tile->header->tx == tx &&
+			tile->header->ty == ty &&
+			tile->header->tlayer == tlayer)
+		{
+			return tile;
+		}
+		tile = tile->next;
+	}
+	return 0;
+}
+
+dtCompressedTileRef dtTileCache::getTileRef(const dtCompressedTile* tile) const
+{
+	if (!tile) return 0;
+	const unsigned int it = (unsigned int)(tile - m_tiles);
+	return (dtCompressedTileRef)encodeTileId(tile->salt, it);
+}
+
+dtObstacleRef dtTileCache::getObstacleRef(const dtTileCacheObstacle* ob) const
+{
+	if (!ob) return 0;
+	const unsigned int idx = (unsigned int)(ob - m_obstacles);
+	return encodeObstacleId(ob->salt, idx);
+}
+
+const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref)
+{
+	if (!ref)
+		return 0;
+	unsigned int idx = decodeObstacleIdObstacle(ref);
+	if ((int)idx >= m_params.maxObstacles)
+		return 0;
+	const dtTileCacheObstacle* ob = &m_obstacles[idx];
+	unsigned int salt = decodeObstacleIdSalt(ref);
+	if (ob->salt != salt)
+		return 0;
+	return ob;
+}
+
+dtStatus dtTileCache::addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result)
+{
+	// Make sure the data is in right format.
+	dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)data;
+	if (header->magic != DT_TILECACHE_MAGIC)
+		return DT_FAILURE | DT_WRONG_MAGIC;
+	if (header->version != DT_TILECACHE_VERSION)
+		return DT_FAILURE | DT_WRONG_VERSION;
+	
+	// Make sure the location is free.
+	if (getTileAt(header->tx, header->ty, header->tlayer))
+		return DT_FAILURE;
+	
+	// Allocate a tile.
+	dtCompressedTile* tile = 0;
+	if (m_nextFreeTile)
+	{
+		tile = m_nextFreeTile;
+		m_nextFreeTile = tile->next;
+		tile->next = 0;
+	}
+	
+	// Make sure we could allocate a tile.
+	if (!tile)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	
+	// Insert tile into the position lut.
+	int h = computeTileHash(header->tx, header->ty, m_tileLutMask);
+	tile->next = m_posLookup[h];
+	m_posLookup[h] = tile;
+	
+	// Init tile.
+	const int headerSize = dtAlign4(sizeof(dtTileCacheLayerHeader));
+	tile->header = (dtTileCacheLayerHeader*)data;
+	tile->data = data;
+	tile->dataSize = dataSize;
+	tile->compressed = tile->data + headerSize;
+	tile->compressedSize = tile->dataSize - headerSize;
+	tile->flags = flags;
+	
+	if (result)
+		*result = getTileRef(tile);
+	
+	return DT_SUCCESS;
+}
+
+dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize)
+{
+	if (!ref)
+		return DT_FAILURE | DT_INVALID_PARAM;
+	unsigned int tileIndex = decodeTileIdTile(ref);
+	unsigned int tileSalt = decodeTileIdSalt(ref);
+	if ((int)tileIndex >= m_params.maxTiles)
+		return DT_FAILURE | DT_INVALID_PARAM;
+	dtCompressedTile* tile = &m_tiles[tileIndex];
+	if (tile->salt != tileSalt)
+		return DT_FAILURE | DT_INVALID_PARAM;
+	
+	// Remove tile from hash lookup.
+	const int h = computeTileHash(tile->header->tx,tile->header->ty,m_tileLutMask);
+	dtCompressedTile* prev = 0;
+	dtCompressedTile* cur = m_posLookup[h];
+	while (cur)
+	{
+		if (cur == tile)
+		{
+			if (prev)
+				prev->next = cur->next;
+			else
+				m_posLookup[h] = cur->next;
+			break;
+		}
+		prev = cur;
+		cur = cur->next;
+	}
+	
+	// Reset tile.
+	if (tile->flags & DT_COMPRESSEDTILE_FREE_DATA)
+	{
+		// Owns data
+		dtFree(tile->data);
+		tile->data = 0;
+		tile->dataSize = 0;
+		if (data) *data = 0;
+		if (dataSize) *dataSize = 0;
+	}
+	else
+	{
+		if (data) *data = tile->data;
+		if (dataSize) *dataSize = tile->dataSize;
+	}
+	
+	tile->header = 0;
+	tile->data = 0;
+	tile->dataSize = 0;
+	tile->compressed = 0;
+	tile->compressedSize = 0;
+	tile->flags = 0;
+	
+	// Update salt, salt should never be zero.
+	tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1);
+	if (tile->salt == 0)
+		tile->salt++;
+	
+	// Add to free list.
+	tile->next = m_nextFreeTile;
+	m_nextFreeTile = tile;
+	
+	return DT_SUCCESS;
+}
+
+
+dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result)
+{
+	if (m_nreqs >= MAX_REQUESTS)
+		return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+	
+	dtTileCacheObstacle* ob = 0;
+	if (m_nextFreeObstacle)
+	{
+		ob = m_nextFreeObstacle;
+		m_nextFreeObstacle = ob->next;
+		ob->next = 0;
+	}
+	if (!ob)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	
+	unsigned short salt = ob->salt;
+	memset(ob, 0, sizeof(dtTileCacheObstacle));
+	ob->salt = salt;
+	ob->state = DT_OBSTACLE_PROCESSING;
+	dtVcopy(ob->pos, pos);
+	ob->radius = radius;
+	ob->height = height;
+	
+	ObstacleRequest* req = &m_reqs[m_nreqs++];
+	memset(req, 0, sizeof(ObstacleRequest));
+	req->action = REQUEST_ADD;
+	req->ref = getObstacleRef(ob);
+	
+	if (result)
+		*result = req->ref;
+	
+	return DT_SUCCESS;
+}
+
+dtObstacleRef dtTileCache::removeObstacle(const dtObstacleRef ref)
+{
+	if (!ref)
+		return DT_SUCCESS;
+	if (m_nreqs >= MAX_REQUESTS)
+		return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+	
+	ObstacleRequest* req = &m_reqs[m_nreqs++];
+	memset(req, 0, sizeof(ObstacleRequest));
+	req->action = REQUEST_REMOVE;
+	req->ref = ref;
+	
+	return DT_SUCCESS;
+}
+
+dtStatus dtTileCache::queryTiles(const float* bmin, const float* bmax,
+								 dtCompressedTileRef* results, int* resultCount, const int maxResults) const 
+{
+	const int MAX_TILES = 32;
+	dtCompressedTileRef tiles[MAX_TILES];
+	
+	int n = 0;
+	
+	const float tw = m_params.width * m_params.cs;
+	const float th = m_params.height * m_params.cs;
+	const int tx0 = (int)dtMathFloorf((bmin[0]-m_params.orig[0]) / tw);
+	const int tx1 = (int)dtMathFloorf((bmax[0]-m_params.orig[0]) / tw);
+	const int ty0 = (int)dtMathFloorf((bmin[2]-m_params.orig[2]) / th);
+	const int ty1 = (int)dtMathFloorf((bmax[2]-m_params.orig[2]) / th);
+	
+	for (int ty = ty0; ty <= ty1; ++ty)
+	{
+		for (int tx = tx0; tx <= tx1; ++tx)
+		{
+			const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
+			
+			for (int i = 0; i < ntiles; ++i)
+			{
+				const dtCompressedTile* tile = &m_tiles[decodeTileIdTile(tiles[i])];
+				float tbmin[3], tbmax[3];
+				calcTightTileBounds(tile->header, tbmin, tbmax);
+				
+				if (dtOverlapBounds(bmin,bmax, tbmin,tbmax))
+				{
+					if (n < maxResults)
+						results[n++] = tiles[i];
+				}
+			}
+		}
+	}
+	
+	*resultCount = n;
+	
+	return DT_SUCCESS;
+}
+
+dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh)
+{
+	if (m_nupdate == 0)
+	{
+		// Process requests.
+		for (int i = 0; i < m_nreqs; ++i)
+		{
+			ObstacleRequest* req = &m_reqs[i];
+			
+			unsigned int idx = decodeObstacleIdObstacle(req->ref);
+			if ((int)idx >= m_params.maxObstacles)
+				continue;
+			dtTileCacheObstacle* ob = &m_obstacles[idx];
+			unsigned int salt = decodeObstacleIdSalt(req->ref);
+			if (ob->salt != salt)
+				continue;
+			
+			if (req->action == REQUEST_ADD)
+			{
+				// Find touched tiles.
+				float bmin[3], bmax[3];
+				getObstacleBounds(ob, bmin, bmax);
+
+				int ntouched = 0;
+				queryTiles(bmin, bmax, ob->touched, &ntouched, DT_MAX_TOUCHED_TILES);
+				ob->ntouched = (unsigned char)ntouched;
+				// Add tiles to update list.
+				ob->npending = 0;
+				for (int j = 0; j < ob->ntouched; ++j)
+				{
+					if (m_nupdate < MAX_UPDATE)
+					{
+						if (!contains(m_update, m_nupdate, ob->touched[j]))
+							m_update[m_nupdate++] = ob->touched[j];
+						ob->pending[ob->npending++] = ob->touched[j];
+					}
+				}
+			}
+			else if (req->action == REQUEST_REMOVE)
+			{
+				// Prepare to remove obstacle.
+				ob->state = DT_OBSTACLE_REMOVING;
+				// Add tiles to update list.
+				ob->npending = 0;
+				for (int j = 0; j < ob->ntouched; ++j)
+				{
+					if (m_nupdate < MAX_UPDATE)
+					{
+						if (!contains(m_update, m_nupdate, ob->touched[j]))
+							m_update[m_nupdate++] = ob->touched[j];
+						ob->pending[ob->npending++] = ob->touched[j];
+					}
+				}
+			}
+		}
+		
+		m_nreqs = 0;
+	}
+	
+	// Process updates
+	if (m_nupdate)
+	{
+		// Build mesh
+		const dtCompressedTileRef ref = m_update[0];
+		dtStatus status = buildNavMeshTile(ref, navmesh);
+		m_nupdate--;
+		if (m_nupdate > 0)
+			memmove(m_update, m_update+1, m_nupdate*sizeof(dtCompressedTileRef));
+
+		// Update obstacle states.
+		for (int i = 0; i < m_params.maxObstacles; ++i)
+		{
+			dtTileCacheObstacle* ob = &m_obstacles[i];
+			if (ob->state == DT_OBSTACLE_PROCESSING || ob->state == DT_OBSTACLE_REMOVING)
+			{
+				// Remove handled tile from pending list.
+				for (int j = 0; j < (int)ob->npending; j++)
+				{
+					if (ob->pending[j] == ref)
+					{
+						ob->pending[j] = ob->pending[(int)ob->npending-1];
+						ob->npending--;
+						break;
+					}
+				}
+				
+				// If all pending tiles processed, change state.
+				if (ob->npending == 0)
+				{
+					if (ob->state == DT_OBSTACLE_PROCESSING)
+					{
+						ob->state = DT_OBSTACLE_PROCESSED;
+					}
+					else if (ob->state == DT_OBSTACLE_REMOVING)
+					{
+						ob->state = DT_OBSTACLE_EMPTY;
+						// Update salt, salt should never be zero.
+						ob->salt = (ob->salt+1) & ((1<<16)-1);
+						if (ob->salt == 0)
+							ob->salt++;
+						// Return obstacle to free list.
+						ob->next = m_nextFreeObstacle;
+						m_nextFreeObstacle = ob;
+					}
+				}
+			}
+		}
+			
+		if (dtStatusFailed(status))
+			return status;
+	}
+	
+	return DT_SUCCESS;
+}
+
+
+dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh)
+{
+	const int MAX_TILES = 32;
+	dtCompressedTileRef tiles[MAX_TILES];
+	const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
+	
+	for (int i = 0; i < ntiles; ++i)
+	{
+		dtStatus status = buildNavMeshTile(tiles[i], navmesh);
+		if (dtStatusFailed(status))
+			return status;
+	}
+	
+	return DT_SUCCESS;
+}
+
+dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh)
+{	
+	dtAssert(m_talloc);
+	dtAssert(m_tcomp);
+	
+	unsigned int idx = decodeTileIdTile(ref);
+	if (idx > (unsigned int)m_params.maxTiles)
+		return DT_FAILURE | DT_INVALID_PARAM;
+	const dtCompressedTile* tile = &m_tiles[idx];
+	unsigned int salt = decodeTileIdSalt(ref);
+	if (tile->salt != salt)
+		return DT_FAILURE | DT_INVALID_PARAM;
+	
+	m_talloc->reset();
+	
+	BuildContext bc(m_talloc);
+	const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch);
+	dtStatus status;
+	
+	// Decompress tile layer data. 
+	status = dtDecompressTileCacheLayer(m_talloc, m_tcomp, tile->data, tile->dataSize, &bc.layer);
+	if (dtStatusFailed(status))
+		return status;
+	
+	// Rasterize obstacles.
+	for (int i = 0; i < m_params.maxObstacles; ++i)
+	{
+		const dtTileCacheObstacle* ob = &m_obstacles[i];
+		if (ob->state == DT_OBSTACLE_EMPTY || ob->state == DT_OBSTACLE_REMOVING)
+			continue;
+		if (contains(ob->touched, ob->ntouched, ref))
+		{
+			dtMarkCylinderArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
+							   ob->pos, ob->radius, ob->height, 0);
+		}
+	}
+	
+	// Build navmesh
+	status = dtBuildTileCacheRegions(m_talloc, *bc.layer, walkableClimbVx);
+	if (dtStatusFailed(status))
+		return status;
+	
+	bc.lcset = dtAllocTileCacheContourSet(m_talloc);
+	if (!bc.lcset)
+		return status;
+	status = dtBuildTileCacheContours(m_talloc, *bc.layer, walkableClimbVx,
+									  m_params.maxSimplificationError, *bc.lcset);
+	if (dtStatusFailed(status))
+		return status;
+	
+	bc.lmesh = dtAllocTileCachePolyMesh(m_talloc);
+	if (!bc.lmesh)
+		return status;
+	status = dtBuildTileCachePolyMesh(m_talloc, *bc.lcset, *bc.lmesh);
+	if (dtStatusFailed(status))
+		return status;
+	
+	// Early out if the mesh tile is empty.
+	if (!bc.lmesh->npolys)
+		return DT_SUCCESS;
+	
+	dtNavMeshCreateParams params;
+	memset(&params, 0, sizeof(params));
+	params.verts = bc.lmesh->verts;
+	params.vertCount = bc.lmesh->nverts;
+	params.polys = bc.lmesh->polys;
+	params.polyAreas = bc.lmesh->areas;
+	params.polyFlags = bc.lmesh->flags;
+	params.polyCount = bc.lmesh->npolys;
+	params.nvp = DT_VERTS_PER_POLYGON;
+	params.walkableHeight = m_params.walkableHeight;
+	params.walkableRadius = m_params.walkableRadius;
+	params.walkableClimb = m_params.walkableClimb;
+	params.tileX = tile->header->tx;
+	params.tileY = tile->header->ty;
+	params.tileLayer = tile->header->tlayer;
+	params.cs = m_params.cs;
+	params.ch = m_params.ch;
+	params.buildBvTree = false;
+	dtVcopy(params.bmin, tile->header->bmin);
+	dtVcopy(params.bmax, tile->header->bmax);
+	
+	if (m_tmproc)
+	{
+		m_tmproc->process(&params, bc.lmesh->areas, bc.lmesh->flags);
+	}
+	
+	unsigned char* navData = 0;
+	int navDataSize = 0;
+	if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
+		return DT_FAILURE;
+
+	// Remove existing tile.
+	navmesh->removeTile(navmesh->getTileRefAt(tile->header->tx,tile->header->ty,tile->header->tlayer),0,0);
+
+	// Add new tile, or leave the location empty.
+	if (navData)
+	{
+		// Let the navmesh own the data.
+		status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0);
+		if (dtStatusFailed(status))
+		{
+			dtFree(navData);
+			return status;
+		}
+	}
+	
+	return DT_SUCCESS;
+}
+
+void dtTileCache::calcTightTileBounds(const dtTileCacheLayerHeader* header, float* bmin, float* bmax) const
+{
+	const float cs = m_params.cs;
+	bmin[0] = header->bmin[0] + header->minx*cs;
+	bmin[1] = header->bmin[1];
+	bmin[2] = header->bmin[2] + header->miny*cs;
+	bmax[0] = header->bmin[0] + (header->maxx+1)*cs;
+	bmax[1] = header->bmax[1];
+	bmax[2] = header->bmin[2] + (header->maxy+1)*cs;
+}
+
+void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const
+{
+	bmin[0] = ob->pos[0] - ob->radius;
+	bmin[1] = ob->pos[1];
+	bmin[2] = ob->pos[2] - ob->radius;
+	bmax[0] = ob->pos[0] + ob->radius;
+	bmax[1] = ob->pos[1] + ob->height;
+	bmax[2] = ob->pos[2] + ob->radius;	
+}

+ 2151 - 0
Source/ThirdParty/DetourTileCache/source/DetourTileCacheBuilder.cpp

@@ -0,0 +1,2151 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// This software is provided 'as-is', without any express or implied
+// warranty.  In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include "DetourCommon.h"
+#include "DetourMath.h"
+#include "DetourStatus.h"
+#include "DetourAssert.h"
+#include "DetourTileCacheBuilder.h"
+#include <string.h>
+
+
+template<class T> class dtFixedArray
+{
+	dtTileCacheAlloc* m_alloc;
+	T* m_ptr;
+	const int m_size;
+	inline T* operator=(T* p);
+	inline void operator=(dtFixedArray<T>& p);
+	inline dtFixedArray();
+public:
+	inline dtFixedArray(dtTileCacheAlloc* a, const int s) : m_alloc(a), m_ptr((T*)a->alloc(sizeof(T)*s)), m_size(s) {}
+	inline ~dtFixedArray() { if (m_alloc) m_alloc->free(m_ptr); }
+	inline operator T*() { return m_ptr; }
+	inline int size() const { return m_size; }
+};
+
+inline int getDirOffsetX(int dir)
+{
+	const int offset[4] = { -1, 0, 1, 0, };
+	return offset[dir&0x03];
+}
+
+inline int getDirOffsetY(int dir)
+{
+	const int offset[4] = { 0, 1, 0, -1 };
+	return offset[dir&0x03];
+}
+
+static const int MAX_VERTS_PER_POLY = 6;	// TODO: use the DT_VERTS_PER_POLYGON
+static const int MAX_REM_EDGES = 48;		// TODO: make this an expression.
+
+
+
+dtTileCacheContourSet* dtAllocTileCacheContourSet(dtTileCacheAlloc* alloc)
+{
+	dtAssert(alloc);
+
+	dtTileCacheContourSet* cset = (dtTileCacheContourSet*)alloc->alloc(sizeof(dtTileCacheContourSet));
+	memset(cset, 0, sizeof(dtTileCacheContourSet));
+	return cset;
+}
+
+void dtFreeTileCacheContourSet(dtTileCacheAlloc* alloc, dtTileCacheContourSet* cset)
+{
+	dtAssert(alloc);
+
+	if (!cset) return;
+	for (int i = 0; i < cset->nconts; ++i)
+		alloc->free(cset->conts[i].verts);
+	alloc->free(cset->conts);
+	alloc->free(cset);
+}
+
+dtTileCachePolyMesh* dtAllocTileCachePolyMesh(dtTileCacheAlloc* alloc)
+{
+	dtAssert(alloc);
+
+	dtTileCachePolyMesh* lmesh = (dtTileCachePolyMesh*)alloc->alloc(sizeof(dtTileCachePolyMesh));
+	memset(lmesh, 0, sizeof(dtTileCachePolyMesh));
+	return lmesh;
+}
+
+void dtFreeTileCachePolyMesh(dtTileCacheAlloc* alloc, dtTileCachePolyMesh* lmesh)
+{
+	dtAssert(alloc);
+	
+	if (!lmesh) return;
+	alloc->free(lmesh->verts);
+	alloc->free(lmesh->polys);
+	alloc->free(lmesh->flags);
+	alloc->free(lmesh->areas);
+	alloc->free(lmesh);
+}
+
+
+
+struct dtLayerSweepSpan
+{
+	unsigned short ns;	// number samples
+	unsigned char id;	// region id
+	unsigned char nei;	// neighbour id
+};
+
+static const int DT_LAYER_MAX_NEIS = 16;
+
+struct dtLayerMonotoneRegion
+{
+	int area;
+	unsigned char neis[DT_LAYER_MAX_NEIS];
+	unsigned char nneis;
+	unsigned char regId;
+	unsigned char areaId;
+};
+
+struct dtTempContour
+{
+	inline dtTempContour(unsigned char* vbuf, const int nvbuf,
+						 unsigned short* pbuf, const int npbuf) :
+		verts(vbuf), nverts(0), cverts(nvbuf),
+		poly(pbuf), npoly(0), cpoly(npbuf) 
+	{
+	}
+	unsigned char* verts;
+	int nverts;
+	int cverts;
+	unsigned short* poly;
+	int npoly;
+	int cpoly;
+};
+
+
+
+
+inline bool overlapRangeExl(const unsigned short amin, const unsigned short amax,
+							const unsigned short bmin, const unsigned short bmax)
+{
+	return (amin >= bmax || amax <= bmin) ? false : true;
+}
+
+static void addUniqueLast(unsigned char* a, unsigned char& an, unsigned char v)
+{
+	const int n = (int)an;
+	if (n > 0 && a[n-1] == v) return;
+	a[an] = v;
+	an++;
+}
+
+inline bool isConnected(const dtTileCacheLayer& layer,
+						const int ia, const int ib, const int walkableClimb)
+{
+	if (layer.areas[ia] != layer.areas[ib]) return false;
+	if (dtAbs((int)layer.heights[ia] - (int)layer.heights[ib]) > walkableClimb) return false;
+	return true;
+}
+
+static bool canMerge(unsigned char oldRegId, unsigned char newRegId, const dtLayerMonotoneRegion* regs, const int nregs)
+{
+	int count = 0;
+	for (int i = 0; i < nregs; ++i)
+	{
+		const dtLayerMonotoneRegion& reg = regs[i];
+		if (reg.regId != oldRegId) continue;
+		const int nnei = (int)reg.nneis;
+		for (int j = 0; j < nnei; ++j)
+		{
+			if (regs[reg.neis[j]].regId == newRegId)
+				count++;
+		}
+	}
+	return count == 1;
+}
+
+
+dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc,
+								 dtTileCacheLayer& layer,
+								 const int walkableClimb)
+{
+	dtAssert(alloc);
+	
+	const int w = (int)layer.header->width;
+	const int h = (int)layer.header->height;
+	
+	memset(layer.regs,0xff,sizeof(unsigned char)*w*h);
+	
+	const int nsweeps = w;
+	dtFixedArray<dtLayerSweepSpan> sweeps(alloc, nsweeps);
+	if (!sweeps)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memset(sweeps,0,sizeof(dtLayerSweepSpan)*nsweeps);
+	
+	// Partition walkable area into monotone regions.
+	unsigned char prevCount[256];
+	unsigned char regId = 0;
+	
+	for (int y = 0; y < h; ++y)
+	{
+		if (regId > 0)
+			memset(prevCount,0,sizeof(unsigned char)*regId);
+		unsigned char sweepId = 0;
+		
+		for (int x = 0; x < w; ++x)
+		{
+			const int idx = x + y*w;
+			if (layer.areas[idx] == DT_TILECACHE_NULL_AREA) continue;
+			
+			unsigned char sid = 0xff;
+			
+			// -x
+			const int xidx = (x-1)+y*w;
+			if (x > 0 && isConnected(layer, idx, xidx, walkableClimb))
+			{
+				if (layer.regs[xidx] != 0xff)
+					sid = layer.regs[xidx];
+			}
+			
+			if (sid == 0xff)
+			{
+				sid = sweepId++;
+				sweeps[sid].nei = 0xff;
+				sweeps[sid].ns = 0;
+			}
+			
+			// -y
+			const int yidx = x+(y-1)*w;
+			if (y > 0 && isConnected(layer, idx, yidx, walkableClimb))
+			{
+				const unsigned char nr = layer.regs[yidx];
+				if (nr != 0xff)
+				{
+					// Set neighbour when first valid neighbour is encoutered.
+					if (sweeps[sid].ns == 0)
+						sweeps[sid].nei = nr;
+					
+					if (sweeps[sid].nei == nr)
+					{
+						// Update existing neighbour
+						sweeps[sid].ns++;
+						prevCount[nr]++;
+					}
+					else
+					{
+						// This is hit if there is nore than one neighbour.
+						// Invalidate the neighbour.
+						sweeps[sid].nei = 0xff;
+					}
+				}
+			}
+			
+			layer.regs[idx] = sid;
+		}
+		
+		// Create unique ID.
+		for (int i = 0; i < sweepId; ++i)
+		{
+			// If the neighbour is set and there is only one continuous connection to it,
+			// the sweep will be merged with the previous one, else new region is created.
+			if (sweeps[i].nei != 0xff && (unsigned short)prevCount[sweeps[i].nei] == sweeps[i].ns)
+			{
+				sweeps[i].id = sweeps[i].nei;
+			}
+			else
+			{
+				if (regId == 255)
+				{
+					// Region ID's overflow.
+					return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+				}
+				sweeps[i].id = regId++;
+			}
+		}
+		
+		// Remap local sweep ids to region ids.
+		for (int x = 0; x < w; ++x)
+		{
+			const int idx = x+y*w;
+			if (layer.regs[idx] != 0xff)
+				layer.regs[idx] = sweeps[layer.regs[idx]].id;
+		}
+	}
+	
+	// Allocate and init layer regions.
+	const int nregs = (int)regId;
+	dtFixedArray<dtLayerMonotoneRegion> regs(alloc, nregs);
+	if (!regs)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+
+	memset(regs, 0, sizeof(dtLayerMonotoneRegion)*nregs);
+	for (int i = 0; i < nregs; ++i)
+		regs[i].regId = 0xff;
+	
+	// Find region neighbours.
+	for (int y = 0; y < h; ++y)
+	{
+		for (int x = 0; x < w; ++x)
+		{
+			const int idx = x+y*w;
+			const unsigned char ri = layer.regs[idx];
+			if (ri == 0xff)
+				continue;
+			
+			// Update area.
+			regs[ri].area++;
+			regs[ri].areaId = layer.areas[idx];
+			
+			// Update neighbours
+			const int ymi = x+(y-1)*w;
+			if (y > 0 && isConnected(layer, idx, ymi, walkableClimb))
+			{
+				const unsigned char rai = layer.regs[ymi];
+				if (rai != 0xff && rai != ri)
+				{
+					addUniqueLast(regs[ri].neis, regs[ri].nneis, rai);
+					addUniqueLast(regs[rai].neis, regs[rai].nneis, ri);
+				}
+			}
+		}
+	}
+	
+	for (int i = 0; i < nregs; ++i)
+		regs[i].regId = (unsigned char)i;
+	
+	for (int i = 0; i < nregs; ++i)
+	{
+		dtLayerMonotoneRegion& reg = regs[i];
+		
+		int merge = -1;
+		int mergea = 0;
+		for (int j = 0; j < (int)reg.nneis; ++j)
+		{
+			const unsigned char nei = reg.neis[j];
+			dtLayerMonotoneRegion& regn = regs[nei];
+			if (reg.regId == regn.regId)
+				continue;
+			if (reg.areaId != regn.areaId)
+				continue;
+			if (regn.area > mergea)
+			{
+				if (canMerge(reg.regId, regn.regId, regs, nregs))
+				{
+					mergea = regn.area;
+					merge = (int)nei;
+				}
+			}
+		}
+		if (merge != -1)
+		{
+			const unsigned char oldId = reg.regId;
+			const unsigned char newId = regs[merge].regId;
+			for (int j = 0; j < nregs; ++j)
+				if (regs[j].regId == oldId)
+					regs[j].regId = newId;
+		}
+	}
+	
+	// Compact ids.
+	unsigned char remap[256];
+	memset(remap, 0, 256);
+	// Find number of unique regions.
+	regId = 0;
+	for (int i = 0; i < nregs; ++i)
+		remap[regs[i].regId] = 1;
+	for (int i = 0; i < 256; ++i)
+		if (remap[i])
+			remap[i] = regId++;
+	// Remap ids.
+	for (int i = 0; i < nregs; ++i)
+		regs[i].regId = remap[regs[i].regId];
+	
+	layer.regCount = regId;
+	
+	for (int i = 0; i < w*h; ++i)
+	{
+		if (layer.regs[i] != 0xff)
+			layer.regs[i] = regs[layer.regs[i]].regId;
+	}
+	
+	return DT_SUCCESS;
+}
+
+
+
+static bool appendVertex(dtTempContour& cont, const int x, const int y, const int z, const int r)
+{
+	// Try to merge with existing segments.
+	if (cont.nverts > 1)
+	{
+		unsigned char* pa = &cont.verts[(cont.nverts-2)*4];
+		unsigned char* pb = &cont.verts[(cont.nverts-1)*4];
+		if ((int)pb[3] == r)
+		{
+			if (pa[0] == pb[0] && (int)pb[0] == x)
+			{
+				// The verts are aligned aling x-axis, update z.
+				pb[1] = (unsigned char)y;
+				pb[2] = (unsigned char)z;
+				return true;
+			}
+			else if (pa[2] == pb[2] && (int)pb[2] == z)
+			{
+				// The verts are aligned aling z-axis, update x.
+				pb[0] = (unsigned char)x;
+				pb[1] = (unsigned char)y;
+				return true;
+			}
+		}
+	}
+	
+	// Add new point.
+	if (cont.nverts+1 > cont.cverts)
+		return false;
+	
+	unsigned char* v = &cont.verts[cont.nverts*4];
+	v[0] = (unsigned char)x;
+	v[1] = (unsigned char)y;
+	v[2] = (unsigned char)z;
+	v[3] = (unsigned char)r;
+	cont.nverts++;
+	
+	return true;
+}
+
+
+static unsigned char getNeighbourReg(dtTileCacheLayer& layer,
+									 const int ax, const int ay, const int dir)
+{
+	const int w = (int)layer.header->width;
+	const int ia = ax + ay*w;
+	
+	const unsigned char con = layer.cons[ia] & 0xf;
+	const unsigned char portal = layer.cons[ia] >> 4;
+	const unsigned char mask = (unsigned char)(1<<dir);
+	
+	if ((con & mask) == 0)
+	{
+		// No connection, return portal or hard edge.
+		if (portal & mask)
+			return 0xf8 + (unsigned char)dir;
+		return 0xff;
+	}
+	
+	const int bx = ax + getDirOffsetX(dir);
+	const int by = ay + getDirOffsetY(dir);
+	const int ib = bx + by*w;
+	
+	return layer.regs[ib];
+}
+
+static bool walkContour(dtTileCacheLayer& layer, int x, int y, dtTempContour& cont)
+{
+	const int w = (int)layer.header->width;
+	const int h = (int)layer.header->height;
+	
+	cont.nverts = 0;
+	
+	int startX = x;
+	int startY = y;
+	int startDir = -1;
+	
+	for (int i = 0; i < 4; ++i)
+	{
+		const int dir = (i+3)&3;
+		unsigned char rn = getNeighbourReg(layer, x, y, dir);
+		if (rn != layer.regs[x+y*w])
+		{
+			startDir = dir;
+			break;
+		}
+	}
+	if (startDir == -1)
+		return true;
+	
+	int dir = startDir;
+	const int maxIter = w*h;
+	
+	int iter = 0;
+	while (iter < maxIter)
+	{
+		unsigned char rn = getNeighbourReg(layer, x, y, dir);
+		
+		int nx = x;
+		int ny = y;
+		int ndir = dir;
+		
+		if (rn != layer.regs[x+y*w])
+		{
+			// Solid edge.
+			int px = x;
+			int pz = y;
+			switch(dir)
+			{
+				case 0: pz++; break;
+				case 1: px++; pz++; break;
+				case 2: px++; break;
+			}
+			
+			// Try to merge with previous vertex.
+			if (!appendVertex(cont, px, (int)layer.heights[x+y*w], pz,rn))
+				return false;
+			
+			ndir = (dir+1) & 0x3;  // Rotate CW
+		}
+		else
+		{
+			// Move to next.
+			nx = x + getDirOffsetX(dir);
+			ny = y + getDirOffsetY(dir);
+			ndir = (dir+3) & 0x3;	// Rotate CCW
+		}
+		
+		if (iter > 0 && x == startX && y == startY && dir == startDir)
+			break;
+		
+		x = nx;
+		y = ny;
+		dir = ndir;
+		
+		iter++;
+	}
+	
+	// Remove last vertex if it is duplicate of the first one.
+	unsigned char* pa = &cont.verts[(cont.nverts-1)*4];
+	unsigned char* pb = &cont.verts[0];
+	if (pa[0] == pb[0] && pa[2] == pb[2])
+		cont.nverts--;
+	
+	return true;
+}	
+
+
+static float distancePtSeg(const int x, const int z,
+						   const int px, const int pz,
+						   const int qx, const int qz)
+{
+	float pqx = (float)(qx - px);
+	float pqz = (float)(qz - pz);
+	float dx = (float)(x - px);
+	float dz = (float)(z - pz);
+	float d = pqx*pqx + pqz*pqz;
+	float t = pqx*dx + pqz*dz;
+	if (d > 0)
+		t /= d;
+	if (t < 0)
+		t = 0;
+	else if (t > 1)
+		t = 1;
+	
+	dx = px + t*pqx - x;
+	dz = pz + t*pqz - z;
+	
+	return dx*dx + dz*dz;
+}
+
+static void simplifyContour(dtTempContour& cont, const float maxError)
+{
+	cont.npoly = 0;
+	
+	for (int i = 0; i < cont.nverts; ++i)
+	{
+		int j = (i+1) % cont.nverts;
+		// Check for start of a wall segment.
+		unsigned char ra = cont.verts[j*4+3];
+		unsigned char rb = cont.verts[i*4+3];
+		if (ra != rb)
+			cont.poly[cont.npoly++] = (unsigned short)i;
+	}
+	if (cont.npoly < 2)
+	{
+		// If there is no transitions at all,
+		// create some initial points for the simplification process. 
+		// Find lower-left and upper-right vertices of the contour.
+		int llx = cont.verts[0];
+		int llz = cont.verts[2];
+		int lli = 0;
+		int urx = cont.verts[0];
+		int urz = cont.verts[2];
+		int uri = 0;
+		for (int i = 1; i < cont.nverts; ++i)
+		{
+			int x = cont.verts[i*4+0];
+			int z = cont.verts[i*4+2];
+			if (x < llx || (x == llx && z < llz))
+			{
+				llx = x;
+				llz = z;
+				lli = i;
+			}
+			if (x > urx || (x == urx && z > urz))
+			{
+				urx = x;
+				urz = z;
+				uri = i;
+			}
+		}
+		cont.npoly = 0;
+		cont.poly[cont.npoly++] = (unsigned short)lli;
+		cont.poly[cont.npoly++] = (unsigned short)uri;
+	}
+	
+	// Add points until all raw points are within
+	// error tolerance to the simplified shape.
+	for (int i = 0; i < cont.npoly; )
+	{
+		int ii = (i+1) % cont.npoly;
+		
+		const int ai = (int)cont.poly[i];
+		const int ax = (int)cont.verts[ai*4+0];
+		const int az = (int)cont.verts[ai*4+2];
+		
+		const int bi = (int)cont.poly[ii];
+		const int bx = (int)cont.verts[bi*4+0];
+		const int bz = (int)cont.verts[bi*4+2];
+		
+		// Find maximum deviation from the segment.
+		float maxd = 0;
+		int maxi = -1;
+		int ci, cinc, endi;
+		
+		// Traverse the segment in lexilogical order so that the
+		// max deviation is calculated similarly when traversing
+		// opposite segments.
+		if (bx > ax || (bx == ax && bz > az))
+		{
+			cinc = 1;
+			ci = (ai+cinc) % cont.nverts;
+			endi = bi;
+		}
+		else
+		{
+			cinc = cont.nverts-1;
+			ci = (bi+cinc) % cont.nverts;
+			endi = ai;
+		}
+		
+		// Tessellate only outer edges or edges between areas.
+		while (ci != endi)
+		{
+			float d = distancePtSeg(cont.verts[ci*4+0], cont.verts[ci*4+2], ax, az, bx, bz);
+			if (d > maxd)
+			{
+				maxd = d;
+				maxi = ci;
+			}
+			ci = (ci+cinc) % cont.nverts;
+		}
+		
+		
+		// If the max deviation is larger than accepted error,
+		// add new point, else continue to next segment.
+		if (maxi != -1 && maxd > (maxError*maxError))
+		{
+			cont.npoly++;
+			for (int j = cont.npoly-1; j > i; --j)
+				cont.poly[j] = cont.poly[j-1];
+			cont.poly[i+1] = (unsigned short)maxi;
+		}
+		else
+		{
+			++i;
+		}
+	}
+	
+	// Remap vertices
+	int start = 0;
+	for (int i = 1; i < cont.npoly; ++i)
+		if (cont.poly[i] < cont.poly[start])
+			start = i;
+	
+	cont.nverts = 0;
+	for (int i = 0; i < cont.npoly; ++i)
+	{
+		const int j = (start+i) % cont.npoly;
+		unsigned char* src = &cont.verts[cont.poly[j]*4];
+		unsigned char* dst = &cont.verts[cont.nverts*4];
+		dst[0] = src[0];
+		dst[1] = src[1];
+		dst[2] = src[2];
+		dst[3] = src[3];
+		cont.nverts++;
+	}
+}
+
+static unsigned char getCornerHeight(dtTileCacheLayer& layer,
+									 const int x, const int y, const int z,
+									 const int walkableClimb,
+									 bool& shouldRemove)
+{
+	const int w = (int)layer.header->width;
+	const int h = (int)layer.header->height;
+	
+	int n = 0;
+	
+	unsigned char portal = 0xf;
+	unsigned char height = 0;
+	unsigned char preg = 0xff;
+	bool allSameReg = true;
+	
+	for (int dz = -1; dz <= 0; ++dz)
+	{
+		for (int dx = -1; dx <= 0; ++dx)
+		{
+			const int px = x+dx;
+			const int pz = z+dz;
+			if (px >= 0 && pz >= 0 && px < w && pz < h)
+			{
+				const int idx  = px + pz*w;
+				const int lh = (int)layer.heights[idx];
+				if (dtAbs(lh-y) <= walkableClimb && layer.areas[idx] != DT_TILECACHE_NULL_AREA)
+				{
+					height = dtMax(height, (unsigned char)lh);
+					portal &= (layer.cons[idx] >> 4);
+					if (preg != 0xff && preg != layer.regs[idx])
+						allSameReg = false;
+					preg = layer.regs[idx]; 
+					n++;
+				}
+			}
+		}
+	}
+	
+	int portalCount = 0;
+	for (int dir = 0; dir < 4; ++dir)
+		if (portal & (1<<dir))
+			portalCount++;
+	
+	shouldRemove = false;
+	if (n > 1 && portalCount == 1 && allSameReg)
+	{
+		shouldRemove = true;
+	}
+	
+	return height;
+}
+
+
+// TODO: move this somewhere else, once the layer meshing is done.
+dtStatus dtBuildTileCacheContours(dtTileCacheAlloc* alloc,
+								  dtTileCacheLayer& layer,
+								  const int walkableClimb, 	const float maxError,
+								  dtTileCacheContourSet& lcset)
+{
+	dtAssert(alloc);
+
+	const int w = (int)layer.header->width;
+	const int h = (int)layer.header->height;
+	
+	lcset.nconts = layer.regCount;
+	lcset.conts = (dtTileCacheContour*)alloc->alloc(sizeof(dtTileCacheContour)*lcset.nconts);
+	if (!lcset.conts)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memset(lcset.conts, 0, sizeof(dtTileCacheContour)*lcset.nconts);
+	
+	// Allocate temp buffer for contour tracing.
+	const int maxTempVerts = (w+h)*2 * 2; // Twice around the layer.
+	
+	dtFixedArray<unsigned char> tempVerts(alloc, maxTempVerts*4);
+	if (!tempVerts)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	
+	dtFixedArray<unsigned short> tempPoly(alloc, maxTempVerts);
+	if (!tempPoly)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+
+	dtTempContour temp(tempVerts, maxTempVerts, tempPoly, maxTempVerts);
+	
+	// Find contours.
+	for (int y = 0; y < h; ++y)
+	{
+		for (int x = 0; x < w; ++x)
+		{
+			const int idx = x+y*w;
+			const unsigned char ri = layer.regs[idx];
+			if (ri == 0xff)
+				continue;
+			
+			dtTileCacheContour& cont = lcset.conts[ri];
+			
+			if (cont.nverts > 0)
+				continue;
+			
+			cont.reg = ri;
+			cont.area = layer.areas[idx];
+			
+			if (!walkContour(layer, x, y, temp))
+			{
+				// Too complex contour.
+				// Note: If you hit here ofte, try increasing 'maxTempVerts'.
+				return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+			}
+			
+			simplifyContour(temp, maxError);
+			
+			// Store contour.
+			cont.nverts = temp.nverts;
+			if (cont.nverts > 0)
+			{
+				cont.verts = (unsigned char*)alloc->alloc(sizeof(unsigned char)*4*temp.nverts);
+				if (!cont.verts)
+					return DT_FAILURE | DT_OUT_OF_MEMORY;
+				
+				for (int i = 0, j = temp.nverts-1; i < temp.nverts; j=i++)
+				{
+					unsigned char* dst = &cont.verts[j*4];
+					unsigned char* v = &temp.verts[j*4];
+					unsigned char* vn = &temp.verts[i*4];
+					unsigned char nei = vn[3]; // The neighbour reg is stored at segment vertex of a segment. 
+					bool shouldRemove = false;
+					unsigned char lh = getCornerHeight(layer, (int)v[0], (int)v[1], (int)v[2],
+													   walkableClimb, shouldRemove);
+					
+					dst[0] = v[0];
+					dst[1] = lh;
+					dst[2] = v[2];
+					
+					// Store portal direction and remove status to the fourth component.
+					dst[3] = 0x0f;
+					if (nei != 0xff && nei >= 0xf8)
+						dst[3] = nei - 0xf8;
+					if (shouldRemove)
+						dst[3] |= 0x80;
+				}
+			}
+		}
+	}
+	
+	return DT_SUCCESS;
+}	
+
+
+
+static const int VERTEX_BUCKET_COUNT2 = (1<<8);
+
+inline int computeVertexHash2(int x, int y, int z)
+{
+	const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
+	const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
+	const unsigned int h3 = 0xcb1ab31f;
+	unsigned int n = h1 * x + h2 * y + h3 * z;
+	return (int)(n & (VERTEX_BUCKET_COUNT2-1));
+}
+
+static unsigned short addVertex(unsigned short x, unsigned short y, unsigned short z,
+								unsigned short* verts, unsigned short* firstVert, unsigned short* nextVert, int& nv)
+{
+	int bucket = computeVertexHash2(x, 0, z);
+	unsigned short i = firstVert[bucket];
+	
+	while (i != DT_TILECACHE_NULL_IDX)
+	{
+		const unsigned short* v = &verts[i*3];
+		if (v[0] == x && v[2] == z && (dtAbs(v[1] - y) <= 2))
+			return i;
+		i = nextVert[i]; // next
+	}
+	
+	// Could not find, create new.
+	i = (unsigned short)nv; nv++;
+	unsigned short* v = &verts[i*3];
+	v[0] = x;
+	v[1] = y;
+	v[2] = z;
+	nextVert[i] = firstVert[bucket];
+	firstVert[bucket] = i;
+	
+	return (unsigned short)i;
+}
+
+
+struct rcEdge
+{
+	unsigned short vert[2];
+	unsigned short polyEdge[2];
+	unsigned short poly[2];
+};
+
+static bool buildMeshAdjacency(dtTileCacheAlloc* alloc,
+							   unsigned short* polys, const int npolys,
+							   const unsigned short* verts, const int nverts,
+							   const dtTileCacheContourSet& lcset)
+{
+	// Based on code by Eric Lengyel from:
+	// http://www.terathon.com/code/edges.php
+	
+	const int maxEdgeCount = npolys*MAX_VERTS_PER_POLY;
+	dtFixedArray<unsigned short> firstEdge(alloc, nverts + maxEdgeCount);
+	if (!firstEdge)
+		return false;
+	unsigned short* nextEdge = firstEdge + nverts;
+	int edgeCount = 0;
+	
+	dtFixedArray<rcEdge> edges(alloc, maxEdgeCount);
+	if (!edges)
+		return false;
+	
+	for (int i = 0; i < nverts; i++)
+		firstEdge[i] = DT_TILECACHE_NULL_IDX;
+	
+	for (int i = 0; i < npolys; ++i)
+	{
+		unsigned short* t = &polys[i*MAX_VERTS_PER_POLY*2];
+		for (int j = 0; j < MAX_VERTS_PER_POLY; ++j)
+		{
+			if (t[j] == DT_TILECACHE_NULL_IDX) break;
+			unsigned short v0 = t[j];
+			unsigned short v1 = (j+1 >= MAX_VERTS_PER_POLY || t[j+1] == DT_TILECACHE_NULL_IDX) ? t[0] : t[j+1];
+			if (v0 < v1)
+			{
+				rcEdge& edge = edges[edgeCount];
+				edge.vert[0] = v0;
+				edge.vert[1] = v1;
+				edge.poly[0] = (unsigned short)i;
+				edge.polyEdge[0] = (unsigned short)j;
+				edge.poly[1] = (unsigned short)i;
+				edge.polyEdge[1] = 0xff;
+				// Insert edge
+				nextEdge[edgeCount] = firstEdge[v0];
+				firstEdge[v0] = (unsigned short)edgeCount;
+				edgeCount++;
+			}
+		}
+	}
+	
+	for (int i = 0; i < npolys; ++i)
+	{
+		unsigned short* t = &polys[i*MAX_VERTS_PER_POLY*2];
+		for (int j = 0; j < MAX_VERTS_PER_POLY; ++j)
+		{
+			if (t[j] == DT_TILECACHE_NULL_IDX) break;
+			unsigned short v0 = t[j];
+			unsigned short v1 = (j+1 >= MAX_VERTS_PER_POLY || t[j+1] == DT_TILECACHE_NULL_IDX) ? t[0] : t[j+1];
+			if (v0 > v1)
+			{
+				bool found = false;
+				for (unsigned short e = firstEdge[v1]; e != DT_TILECACHE_NULL_IDX; e = nextEdge[e])
+				{
+					rcEdge& edge = edges[e];
+					if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1])
+					{
+						edge.poly[1] = (unsigned short)i;
+						edge.polyEdge[1] = (unsigned short)j;
+						found = true;
+						break;
+					}
+				}
+				if (!found)
+				{
+					// Matching edge not found, it is an open edge, add it.
+					rcEdge& edge = edges[edgeCount];
+					edge.vert[0] = v1;
+					edge.vert[1] = v0;
+					edge.poly[0] = (unsigned short)i;
+					edge.polyEdge[0] = (unsigned short)j;
+					edge.poly[1] = (unsigned short)i;
+					edge.polyEdge[1] = 0xff;
+					// Insert edge
+					nextEdge[edgeCount] = firstEdge[v1];
+					firstEdge[v1] = (unsigned short)edgeCount;
+					edgeCount++;
+				}
+			}
+		}
+	}
+	
+	// Mark portal edges.
+	for (int i = 0; i < lcset.nconts; ++i)
+	{
+		dtTileCacheContour& cont = lcset.conts[i];
+		if (cont.nverts < 3)
+			continue;
+		
+		for (int j = 0, k = cont.nverts-1; j < cont.nverts; k=j++)
+		{
+			const unsigned char* va = &cont.verts[k*4];
+			const unsigned char* vb = &cont.verts[j*4];
+			const unsigned char dir = va[3] & 0xf;
+			if (dir == 0xf)
+				continue;
+			
+			if (dir == 0 || dir == 2)
+			{
+				// Find matching vertical edge
+				const unsigned short x = (unsigned short)va[0];
+				unsigned short zmin = (unsigned short)va[2];
+				unsigned short zmax = (unsigned short)vb[2];
+				if (zmin > zmax)
+					dtSwap(zmin, zmax);
+				
+				for (int m = 0; m < edgeCount; ++m)
+				{
+					rcEdge& e = edges[m];
+					// Skip connected edges.
+					if (e.poly[0] != e.poly[1])
+						continue;
+					const unsigned short* eva = &verts[e.vert[0]*3];
+					const unsigned short* evb = &verts[e.vert[1]*3];
+					if (eva[0] == x && evb[0] == x)
+					{
+						unsigned short ezmin = eva[2];
+						unsigned short ezmax = evb[2];
+						if (ezmin > ezmax)
+							dtSwap(ezmin, ezmax);
+						if (overlapRangeExl(zmin,zmax, ezmin, ezmax))
+						{
+							// Reuse the other polyedge to store dir.
+							e.polyEdge[1] = dir;
+						}
+					}
+				}
+			}
+			else
+			{
+				// Find matching vertical edge
+				const unsigned short z = (unsigned short)va[2];
+				unsigned short xmin = (unsigned short)va[0];
+				unsigned short xmax = (unsigned short)vb[0];
+				if (xmin > xmax)
+					dtSwap(xmin, xmax);
+				for (int m = 0; m < edgeCount; ++m)
+				{
+					rcEdge& e = edges[m];
+					// Skip connected edges.
+					if (e.poly[0] != e.poly[1])
+						continue;
+					const unsigned short* eva = &verts[e.vert[0]*3];
+					const unsigned short* evb = &verts[e.vert[1]*3];
+					if (eva[2] == z && evb[2] == z)
+					{
+						unsigned short exmin = eva[0];
+						unsigned short exmax = evb[0];
+						if (exmin > exmax)
+							dtSwap(exmin, exmax);
+						if (overlapRangeExl(xmin,xmax, exmin, exmax))
+						{
+							// Reuse the other polyedge to store dir.
+							e.polyEdge[1] = dir;
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	
+	// Store adjacency
+	for (int i = 0; i < edgeCount; ++i)
+	{
+		const rcEdge& e = edges[i];
+		if (e.poly[0] != e.poly[1])
+		{
+			unsigned short* p0 = &polys[e.poly[0]*MAX_VERTS_PER_POLY*2];
+			unsigned short* p1 = &polys[e.poly[1]*MAX_VERTS_PER_POLY*2];
+			p0[MAX_VERTS_PER_POLY + e.polyEdge[0]] = e.poly[1];
+			p1[MAX_VERTS_PER_POLY + e.polyEdge[1]] = e.poly[0];
+		}
+		else if (e.polyEdge[1] != 0xff)
+		{
+			unsigned short* p0 = &polys[e.poly[0]*MAX_VERTS_PER_POLY*2];
+			p0[MAX_VERTS_PER_POLY + e.polyEdge[0]] = 0x8000 | (unsigned short)e.polyEdge[1];
+		}
+		
+	}
+	
+	return true;
+}
+
+
+inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
+inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
+
+inline int area2(const unsigned char* a, const unsigned char* b, const unsigned char* c)
+{
+	return ((int)b[0] - (int)a[0]) * ((int)c[2] - (int)a[2]) - ((int)c[0] - (int)a[0]) * ((int)b[2] - (int)a[2]);
+}
+
+//	Exclusive or: true iff exactly one argument is true.
+//	The arguments are negated to ensure that they are 0/1
+//	values.  Then the bitwise Xor operator may apply.
+//	(This idea is due to Michael Baldwin.)
+inline bool xorb(bool x, bool y)
+{
+	return !x ^ !y;
+}
+
+// Returns true iff c is strictly to the left of the directed
+// line through a to b.
+inline bool left(const unsigned char* a, const unsigned char* b, const unsigned char* c)
+{
+	return area2(a, b, c) < 0;
+}
+
+inline bool leftOn(const unsigned char* a, const unsigned char* b, const unsigned char* c)
+{
+	return area2(a, b, c) <= 0;
+}
+
+inline bool collinear(const unsigned char* a, const unsigned char* b, const unsigned char* c)
+{
+	return area2(a, b, c) == 0;
+}
+
+//	Returns true iff ab properly intersects cd: they share
+//	a point interior to both segments.  The properness of the
+//	intersection is ensured by using strict leftness.
+static bool intersectProp(const unsigned char* a, const unsigned char* b,
+						  const unsigned char* c, const unsigned char* d)
+{
+	// Eliminate improper cases.
+	if (collinear(a,b,c) || collinear(a,b,d) ||
+		collinear(c,d,a) || collinear(c,d,b))
+		return false;
+	
+	return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b));
+}
+
+// Returns T iff (a,b,c) are collinear and point c lies 
+// on the closed segement ab.
+static bool between(const unsigned char* a, const unsigned char* b, const unsigned char* c)
+{
+	if (!collinear(a, b, c))
+		return false;
+	// If ab not vertical, check betweenness on x; else on y.
+	if (a[0] != b[0])
+		return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0]));
+	else
+		return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2]));
+}
+
+// Returns true iff segments ab and cd intersect, properly or improperly.
+static bool intersect(const unsigned char* a, const unsigned char* b,
+					  const unsigned char* c, const unsigned char* d)
+{
+	if (intersectProp(a, b, c, d))
+		return true;
+	else if (between(a, b, c) || between(a, b, d) ||
+			 between(c, d, a) || between(c, d, b))
+		return true;
+	else
+		return false;
+}
+
+static bool vequal(const unsigned char* a, const unsigned char* b)
+{
+	return a[0] == b[0] && a[2] == b[2];
+}
+
+// Returns T iff (v_i, v_j) is a proper internal *or* external
+// diagonal of P, *ignoring edges incident to v_i and v_j*.
+static bool diagonalie(int i, int j, int n, const unsigned char* verts, const unsigned short* indices)
+{
+	const unsigned char* d0 = &verts[(indices[i] & 0x7fff) * 4];
+	const unsigned char* d1 = &verts[(indices[j] & 0x7fff) * 4];
+	
+	// For each edge (k,k+1) of P
+	for (int k = 0; k < n; k++)
+	{
+		int k1 = next(k, n);
+		// Skip edges incident to i or j
+		if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
+		{
+			const unsigned char* p0 = &verts[(indices[k] & 0x7fff) * 4];
+			const unsigned char* p1 = &verts[(indices[k1] & 0x7fff) * 4];
+			
+			if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+				continue;
+			
+			if (intersect(d0, d1, p0, p1))
+				return false;
+		}
+	}
+	return true;
+}
+
+// Returns true iff the diagonal (i,j) is strictly internal to the 
+// polygon P in the neighborhood of the i endpoint.
+static bool	inCone(int i, int j, int n, const unsigned char* verts, const unsigned short* indices)
+{
+	const unsigned char* pi = &verts[(indices[i] & 0x7fff) * 4];
+	const unsigned char* pj = &verts[(indices[j] & 0x7fff) * 4];
+	const unsigned char* pi1 = &verts[(indices[next(i, n)] & 0x7fff) * 4];
+	const unsigned char* pin1 = &verts[(indices[prev(i, n)] & 0x7fff) * 4];
+	
+	// If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+	if (leftOn(pin1, pi, pi1))
+		return left(pi, pj, pin1) && left(pj, pi, pi1);
+	// Assume (i-1,i,i+1) not collinear.
+	// else P[i] is reflex.
+	return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+// Returns T iff (v_i, v_j) is a proper internal
+// diagonal of P.
+static bool diagonal(int i, int j, int n, const unsigned char* verts, const unsigned short* indices)
+{
+	return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
+}
+
+static int triangulate(int n, const unsigned char* verts, unsigned short* indices, unsigned short* tris)
+{
+	int ntris = 0;
+	unsigned short* dst = tris;
+	
+	// The last bit of the index is used to indicate if the vertex can be removed.
+	for (int i = 0; i < n; i++)
+	{
+		int i1 = next(i, n);
+		int i2 = next(i1, n);
+		if (diagonal(i, i2, n, verts, indices))
+			indices[i1] |= 0x8000;
+	}
+	
+	while (n > 3)
+	{
+		int minLen = -1;
+		int mini = -1;
+		for (int i = 0; i < n; i++)
+		{
+			int i1 = next(i, n);
+			if (indices[i1] & 0x8000)
+			{
+				const unsigned char* p0 = &verts[(indices[i] & 0x7fff) * 4];
+				const unsigned char* p2 = &verts[(indices[next(i1, n)] & 0x7fff) * 4];
+				
+				const int dx = (int)p2[0] - (int)p0[0];
+				const int dz = (int)p2[2] - (int)p0[2];
+				const int len = dx*dx + dz*dz;
+				if (minLen < 0 || len < minLen)
+				{
+					minLen = len;
+					mini = i;
+				}
+			}
+		}
+		
+		if (mini == -1)
+		{
+			// Should not happen.
+			/*			printf("mini == -1 ntris=%d n=%d\n", ntris, n);
+			 for (int i = 0; i < n; i++)
+			 {
+			 printf("%d ", indices[i] & 0x0fffffff);
+			 }
+			 printf("\n");*/
+			return -ntris;
+		}
+		
+		int i = mini;
+		int i1 = next(i, n);
+		int i2 = next(i1, n);
+		
+		*dst++ = indices[i] & 0x7fff;
+		*dst++ = indices[i1] & 0x7fff;
+		*dst++ = indices[i2] & 0x7fff;
+		ntris++;
+		
+		// Removes P[i1] by copying P[i+1]...P[n-1] left one index.
+		n--;
+		for (int k = i1; k < n; k++)
+			indices[k] = indices[k+1];
+		
+		if (i1 >= n) i1 = 0;
+		i = prev(i1,n);
+		// Update diagonal flags.
+		if (diagonal(prev(i, n), i1, n, verts, indices))
+			indices[i] |= 0x8000;
+		else
+			indices[i] &= 0x7fff;
+		
+		if (diagonal(i, next(i1, n), n, verts, indices))
+			indices[i1] |= 0x8000;
+		else
+			indices[i1] &= 0x7fff;
+	}
+	
+	// Append the remaining triangle.
+	*dst++ = indices[0] & 0x7fff;
+	*dst++ = indices[1] & 0x7fff;
+	*dst++ = indices[2] & 0x7fff;
+	ntris++;
+	
+	return ntris;
+}
+
+
+static int countPolyVerts(const unsigned short* p)
+{
+	for (int i = 0; i < MAX_VERTS_PER_POLY; ++i)
+		if (p[i] == DT_TILECACHE_NULL_IDX)
+			return i;
+	return MAX_VERTS_PER_POLY;
+}
+
+inline bool uleft(const unsigned short* a, const unsigned short* b, const unsigned short* c)
+{
+	return ((int)b[0] - (int)a[0]) * ((int)c[2] - (int)a[2]) -
+	((int)c[0] - (int)a[0]) * ((int)b[2] - (int)a[2]) < 0;
+}
+
+static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
+							 const unsigned short* verts, int& ea, int& eb)
+{
+	const int na = countPolyVerts(pa);
+	const int nb = countPolyVerts(pb);
+	
+	// If the merged polygon would be too big, do not merge.
+	if (na+nb-2 > MAX_VERTS_PER_POLY)
+		return -1;
+	
+	// Check if the polygons share an edge.
+	ea = -1;
+	eb = -1;
+	
+	for (int i = 0; i < na; ++i)
+	{
+		unsigned short va0 = pa[i];
+		unsigned short va1 = pa[(i+1) % na];
+		if (va0 > va1)
+			dtSwap(va0, va1);
+		for (int j = 0; j < nb; ++j)
+		{
+			unsigned short vb0 = pb[j];
+			unsigned short vb1 = pb[(j+1) % nb];
+			if (vb0 > vb1)
+				dtSwap(vb0, vb1);
+			if (va0 == vb0 && va1 == vb1)
+			{
+				ea = i;
+				eb = j;
+				break;
+			}
+		}
+	}
+	
+	// No common edge, cannot merge.
+	if (ea == -1 || eb == -1)
+		return -1;
+	
+	// Check to see if the merged polygon would be convex.
+	unsigned short va, vb, vc;
+	
+	va = pa[(ea+na-1) % na];
+	vb = pa[ea];
+	vc = pb[(eb+2) % nb];
+	if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3]))
+		return -1;
+	
+	va = pb[(eb+nb-1) % nb];
+	vb = pb[eb];
+	vc = pa[(ea+2) % na];
+	if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3]))
+		return -1;
+	
+	va = pa[ea];
+	vb = pa[(ea+1)%na];
+	
+	int dx = (int)verts[va*3+0] - (int)verts[vb*3+0];
+	int dy = (int)verts[va*3+2] - (int)verts[vb*3+2];
+	
+	return dx*dx + dy*dy;
+}
+
+static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb)
+{
+	unsigned short tmp[MAX_VERTS_PER_POLY*2];
+	
+	const int na = countPolyVerts(pa);
+	const int nb = countPolyVerts(pb);
+	
+	// Merge polygons.
+	memset(tmp, 0xff, sizeof(unsigned short)*MAX_VERTS_PER_POLY*2);
+	int n = 0;
+	// Add pa
+	for (int i = 0; i < na-1; ++i)
+		tmp[n++] = pa[(ea+1+i) % na];
+	// Add pb
+	for (int i = 0; i < nb-1; ++i)
+		tmp[n++] = pb[(eb+1+i) % nb];
+	
+	memcpy(pa, tmp, sizeof(unsigned short)*MAX_VERTS_PER_POLY);
+}
+
+
+static void pushFront(unsigned short v, unsigned short* arr, int& an)
+{
+	an++;
+	for (int i = an-1; i > 0; --i)
+		arr[i] = arr[i-1];
+	arr[0] = v;
+}
+
+static void pushBack(unsigned short v, unsigned short* arr, int& an)
+{
+	arr[an] = v;
+	an++;
+}
+
+static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem)
+{
+	// Count number of polygons to remove.
+	int numRemovedVerts = 0;
+	int numTouchedVerts = 0;
+	int numRemainingEdges = 0;
+	for (int i = 0; i < mesh.npolys; ++i)
+	{
+		unsigned short* p = &mesh.polys[i*MAX_VERTS_PER_POLY*2];
+		const int nv = countPolyVerts(p);
+		int numRemoved = 0;
+		int numVerts = 0;
+		for (int j = 0; j < nv; ++j)
+		{
+			if (p[j] == rem)
+			{
+				numTouchedVerts++;
+				numRemoved++;
+			}
+			numVerts++;
+		}
+		if (numRemoved)
+		{
+			numRemovedVerts += numRemoved;
+			numRemainingEdges += numVerts-(numRemoved+1);
+		}
+	}
+	
+	// There would be too few edges remaining to create a polygon.
+	// This can happen for example when a tip of a triangle is marked
+	// as deletion, but there are no other polys that share the vertex.
+	// In this case, the vertex should not be removed.
+	if (numRemainingEdges <= 2)
+		return false;
+	
+	// Check that there is enough memory for the test.
+	const int maxEdges = numTouchedVerts*2;
+	if (maxEdges > MAX_REM_EDGES)
+		return false;
+	
+	// Find edges which share the removed vertex.
+	unsigned short edges[MAX_REM_EDGES];
+	int nedges = 0;
+	
+	for (int i = 0; i < mesh.npolys; ++i)
+	{
+		unsigned short* p = &mesh.polys[i*MAX_VERTS_PER_POLY*2];
+		const int nv = countPolyVerts(p);
+		
+		// Collect edges which touches the removed vertex.
+		for (int j = 0, k = nv-1; j < nv; k = j++)
+		{
+			if (p[j] == rem || p[k] == rem)
+			{
+				// Arrange edge so that a=rem.
+				int a = p[j], b = p[k];
+				if (b == rem)
+					dtSwap(a,b);
+				
+				// Check if the edge exists
+				bool exists = false;
+				for (int m = 0; m < nedges; ++m)
+				{
+					unsigned short* e = &edges[m*3];
+					if (e[1] == b)
+					{
+						// Exists, increment vertex share count.
+						e[2]++;
+						exists = true;
+					}
+				}
+				// Add new edge.
+				if (!exists)
+				{
+					unsigned short* e = &edges[nedges*3];
+					e[0] = (unsigned short)a;
+					e[1] = (unsigned short)b;
+					e[2] = 1;
+					nedges++;
+				}
+			}
+		}
+	}
+	
+	// There should be no more than 2 open edges.
+	// This catches the case that two non-adjacent polygons
+	// share the removed vertex. In that case, do not remove the vertex.
+	int numOpenEdges = 0;
+	for (int i = 0; i < nedges; ++i)
+	{
+		if (edges[i*3+2] < 2)
+			numOpenEdges++;
+	}
+	if (numOpenEdges > 2)
+		return false;
+	
+	return true;
+}
+
+static dtStatus removeVertex(dtTileCachePolyMesh& mesh, const unsigned short rem, const int maxTris)
+{
+	// Count number of polygons to remove.
+	int numRemovedVerts = 0;
+	for (int i = 0; i < mesh.npolys; ++i)
+	{
+		unsigned short* p = &mesh.polys[i*MAX_VERTS_PER_POLY*2];
+		const int nv = countPolyVerts(p);
+		for (int j = 0; j < nv; ++j)
+		{
+			if (p[j] == rem)
+				numRemovedVerts++;
+		}
+	}
+	
+	int nedges = 0;
+	unsigned short edges[MAX_REM_EDGES*3];
+	int nhole = 0;
+	unsigned short hole[MAX_REM_EDGES];
+	int nharea = 0;
+	unsigned short harea[MAX_REM_EDGES];
+	
+	for (int i = 0; i < mesh.npolys; ++i)
+	{
+		unsigned short* p = &mesh.polys[i*MAX_VERTS_PER_POLY*2];
+		const int nv = countPolyVerts(p);
+		bool hasRem = false;
+		for (int j = 0; j < nv; ++j)
+			if (p[j] == rem) hasRem = true;
+		if (hasRem)
+		{
+			// Collect edges which does not touch the removed vertex.
+			for (int j = 0, k = nv-1; j < nv; k = j++)
+			{
+				if (p[j] != rem && p[k] != rem)
+				{
+					if (nedges >= MAX_REM_EDGES)
+						return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+					unsigned short* e = &edges[nedges*3];
+					e[0] = p[k];
+					e[1] = p[j];
+					e[2] = mesh.areas[i];
+					nedges++;
+				}
+			}
+			// Remove the polygon.
+			unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*MAX_VERTS_PER_POLY*2];
+			memcpy(p,p2,sizeof(unsigned short)*MAX_VERTS_PER_POLY);
+			memset(p+MAX_VERTS_PER_POLY,0xff,sizeof(unsigned short)*MAX_VERTS_PER_POLY);
+			mesh.areas[i] = mesh.areas[mesh.npolys-1];
+			mesh.npolys--;
+			--i;
+		}
+	}
+	
+	// Remove vertex.
+	for (int i = (int)rem; i < mesh.nverts; ++i)
+	{
+		mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
+		mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
+		mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2];
+	}
+	mesh.nverts--;
+	
+	// Adjust indices to match the removed vertex layout.
+	for (int i = 0; i < mesh.npolys; ++i)
+	{
+		unsigned short* p = &mesh.polys[i*MAX_VERTS_PER_POLY*2];
+		const int nv = countPolyVerts(p);
+		for (int j = 0; j < nv; ++j)
+			if (p[j] > rem) p[j]--;
+	}
+	for (int i = 0; i < nedges; ++i)
+	{
+		if (edges[i*3+0] > rem) edges[i*3+0]--;
+		if (edges[i*3+1] > rem) edges[i*3+1]--;
+	}
+	
+	if (nedges == 0)
+		return DT_SUCCESS;
+	
+	// Start with one vertex, keep appending connected
+	// segments to the start and end of the hole.
+	pushBack(edges[0], hole, nhole);
+	pushBack(edges[2], harea, nharea);
+	
+	while (nedges)
+	{
+		bool match = false;
+		
+		for (int i = 0; i < nedges; ++i)
+		{
+			const unsigned short ea = edges[i*3+0];
+			const unsigned short eb = edges[i*3+1];
+			const unsigned short a = edges[i*3+2];
+			bool add = false;
+			if (hole[0] == eb)
+			{
+				// The segment matches the beginning of the hole boundary.
+				if (nhole >= MAX_REM_EDGES)
+					return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+				pushFront(ea, hole, nhole);
+				pushFront(a, harea, nharea);
+				add = true;
+			}
+			else if (hole[nhole-1] == ea)
+			{
+				// The segment matches the end of the hole boundary.
+				if (nhole >= MAX_REM_EDGES)
+					return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+				pushBack(eb, hole, nhole);
+				pushBack(a, harea, nharea);
+				add = true;
+			}
+			if (add)
+			{
+				// The edge segment was added, remove it.
+				edges[i*3+0] = edges[(nedges-1)*3+0];
+				edges[i*3+1] = edges[(nedges-1)*3+1];
+				edges[i*3+2] = edges[(nedges-1)*3+2];
+				--nedges;
+				match = true;
+				--i;
+			}
+		}
+		
+		if (!match)
+			break;
+	}
+	
+	
+	unsigned short tris[MAX_REM_EDGES*3];
+	unsigned char tverts[MAX_REM_EDGES*3];
+	unsigned short tpoly[MAX_REM_EDGES*3];
+	
+	// Generate temp vertex array for triangulation.
+	for (int i = 0; i < nhole; ++i)
+	{
+		const unsigned short pi = hole[i];
+		tverts[i*4+0] = (unsigned char)mesh.verts[pi*3+0];
+		tverts[i*4+1] = (unsigned char)mesh.verts[pi*3+1];
+		tverts[i*4+2] = (unsigned char)mesh.verts[pi*3+2];
+		tverts[i*4+3] = 0;
+		tpoly[i] = (unsigned short)i;
+	}
+	
+	// Triangulate the hole.
+	int ntris = triangulate(nhole, tverts, tpoly, tris);
+	if (ntris < 0)
+	{
+		// TODO: issue warning!
+		ntris = -ntris;
+	}
+	
+	if (ntris > MAX_REM_EDGES)
+		return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+	
+	unsigned short polys[MAX_REM_EDGES*MAX_VERTS_PER_POLY];
+	unsigned char pareas[MAX_REM_EDGES];
+	
+	// Build initial polygons.
+	int npolys = 0;
+	memset(polys, 0xff, ntris*MAX_VERTS_PER_POLY*sizeof(unsigned short));
+	for (int j = 0; j < ntris; ++j)
+	{
+		unsigned short* t = &tris[j*3];
+		if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
+		{
+			polys[npolys*MAX_VERTS_PER_POLY+0] = hole[t[0]];
+			polys[npolys*MAX_VERTS_PER_POLY+1] = hole[t[1]];
+			polys[npolys*MAX_VERTS_PER_POLY+2] = hole[t[2]];
+			pareas[npolys] = (unsigned char)harea[t[0]];
+			npolys++;
+		}
+	}
+	if (!npolys)
+		return DT_SUCCESS;
+	
+	// Merge polygons.
+	int maxVertsPerPoly = MAX_VERTS_PER_POLY;
+	if (maxVertsPerPoly > 3)
+	{
+		for (;;)
+		{
+			// Find best polygons to merge.
+			int bestMergeVal = 0;
+			int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
+			
+			for (int j = 0; j < npolys-1; ++j)
+			{
+				unsigned short* pj = &polys[j*MAX_VERTS_PER_POLY];
+				for (int k = j+1; k < npolys; ++k)
+				{
+					unsigned short* pk = &polys[k*MAX_VERTS_PER_POLY];
+					int ea, eb;
+					int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb);
+					if (v > bestMergeVal)
+					{
+						bestMergeVal = v;
+						bestPa = j;
+						bestPb = k;
+						bestEa = ea;
+						bestEb = eb;
+					}
+				}
+			}
+			
+			if (bestMergeVal > 0)
+			{
+				// Found best, merge.
+				unsigned short* pa = &polys[bestPa*MAX_VERTS_PER_POLY];
+				unsigned short* pb = &polys[bestPb*MAX_VERTS_PER_POLY];
+				mergePolys(pa, pb, bestEa, bestEb);
+				memcpy(pb, &polys[(npolys-1)*MAX_VERTS_PER_POLY], sizeof(unsigned short)*MAX_VERTS_PER_POLY);
+				pareas[bestPb] = pareas[npolys-1];
+				npolys--;
+			}
+			else
+			{
+				// Could not merge any polygons, stop.
+				break;
+			}
+		}
+	}
+	
+	// Store polygons.
+	for (int i = 0; i < npolys; ++i)
+	{
+		if (mesh.npolys >= maxTris) break;
+		unsigned short* p = &mesh.polys[mesh.npolys*MAX_VERTS_PER_POLY*2];
+		memset(p,0xff,sizeof(unsigned short)*MAX_VERTS_PER_POLY*2);
+		for (int j = 0; j < MAX_VERTS_PER_POLY; ++j)
+			p[j] = polys[i*MAX_VERTS_PER_POLY+j];
+		mesh.areas[mesh.npolys] = pareas[i];
+		mesh.npolys++;
+		if (mesh.npolys > maxTris)
+			return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+	}
+	
+	return DT_SUCCESS;
+}
+
+
+dtStatus dtBuildTileCachePolyMesh(dtTileCacheAlloc* alloc,
+								  dtTileCacheContourSet& lcset,
+								  dtTileCachePolyMesh& mesh)
+{
+	dtAssert(alloc);
+	
+	int maxVertices = 0;
+	int maxTris = 0;
+	int maxVertsPerCont = 0;
+	for (int i = 0; i < lcset.nconts; ++i)
+	{
+		// Skip null contours.
+		if (lcset.conts[i].nverts < 3) continue;
+		maxVertices += lcset.conts[i].nverts;
+		maxTris += lcset.conts[i].nverts - 2;
+		maxVertsPerCont = dtMax(maxVertsPerCont, lcset.conts[i].nverts);
+	}
+
+	// TODO: warn about too many vertices?
+	
+	mesh.nvp = MAX_VERTS_PER_POLY;
+	
+	dtFixedArray<unsigned char> vflags(alloc, maxVertices);
+	if (!vflags)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memset(vflags, 0, maxVertices);
+	
+	mesh.verts = (unsigned short*)alloc->alloc(sizeof(unsigned short)*maxVertices*3);
+	if (!mesh.verts)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	
+	mesh.polys = (unsigned short*)alloc->alloc(sizeof(unsigned short)*maxTris*MAX_VERTS_PER_POLY*2);
+	if (!mesh.polys)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+
+	mesh.areas = (unsigned char*)alloc->alloc(sizeof(unsigned char)*maxTris);
+	if (!mesh.areas)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+
+	mesh.flags = (unsigned short*)alloc->alloc(sizeof(unsigned short)*maxTris);
+	if (!mesh.flags)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+
+	// Just allocate and clean the mesh flags array. The user is resposible for filling it.
+	memset(mesh.flags, 0, sizeof(unsigned short) * maxTris);
+		
+	mesh.nverts = 0;
+	mesh.npolys = 0;
+	
+	memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3);
+	memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*MAX_VERTS_PER_POLY*2);
+	memset(mesh.areas, 0, sizeof(unsigned char)*maxTris);
+	
+	unsigned short firstVert[VERTEX_BUCKET_COUNT2];
+	for (int i = 0; i < VERTEX_BUCKET_COUNT2; ++i)
+		firstVert[i] = DT_TILECACHE_NULL_IDX;
+	
+	dtFixedArray<unsigned short> nextVert(alloc, maxVertices);
+	if (!nextVert)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memset(nextVert, 0, sizeof(unsigned short)*maxVertices);
+	
+	dtFixedArray<unsigned short> indices(alloc, maxVertsPerCont);
+	if (!indices)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	
+	dtFixedArray<unsigned short> tris(alloc, maxVertsPerCont*3);
+	if (!tris)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+
+	dtFixedArray<unsigned short> polys(alloc, maxVertsPerCont*MAX_VERTS_PER_POLY);
+	if (!polys)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	
+	for (int i = 0; i < lcset.nconts; ++i)
+	{
+		dtTileCacheContour& cont = lcset.conts[i];
+		
+		// Skip null contours.
+		if (cont.nverts < 3)
+			continue;
+		
+		// Triangulate contour
+		for (int j = 0; j < cont.nverts; ++j)
+			indices[j] = (unsigned short)j;
+		
+		int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]);
+		if (ntris <= 0)
+		{
+			// TODO: issue warning!
+			ntris = -ntris;
+		}
+		
+		// Add and merge vertices.
+		for (int j = 0; j < cont.nverts; ++j)
+		{
+			const unsigned char* v = &cont.verts[j*4];
+			indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2],
+								   mesh.verts, firstVert, nextVert, mesh.nverts);
+			if (v[3] & 0x80)
+			{
+				// This vertex should be removed.
+				vflags[indices[j]] = 1;
+			}
+		}
+		
+		// Build initial polygons.
+		int npolys = 0;
+		memset(polys, 0xff, sizeof(unsigned short) * maxVertsPerCont * MAX_VERTS_PER_POLY);
+		for (int j = 0; j < ntris; ++j)
+		{
+			const unsigned short* t = &tris[j*3];
+			if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
+			{
+				polys[npolys*MAX_VERTS_PER_POLY+0] = indices[t[0]];
+				polys[npolys*MAX_VERTS_PER_POLY+1] = indices[t[1]];
+				polys[npolys*MAX_VERTS_PER_POLY+2] = indices[t[2]];
+				npolys++;
+			}
+		}
+		if (!npolys)
+			continue;
+		
+		// Merge polygons.
+		int maxVertsPerPoly =MAX_VERTS_PER_POLY ;
+		if (maxVertsPerPoly > 3)
+		{
+			for(;;)
+			{
+				// Find best polygons to merge.
+				int bestMergeVal = 0;
+				int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
+				
+				for (int j = 0; j < npolys-1; ++j)
+				{
+					unsigned short* pj = &polys[j*MAX_VERTS_PER_POLY];
+					for (int k = j+1; k < npolys; ++k)
+					{
+						unsigned short* pk = &polys[k*MAX_VERTS_PER_POLY];
+						int ea, eb;
+						int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb);
+						if (v > bestMergeVal)
+						{
+							bestMergeVal = v;
+							bestPa = j;
+							bestPb = k;
+							bestEa = ea;
+							bestEb = eb;
+						}
+					}
+				}
+				
+				if (bestMergeVal > 0)
+				{
+					// Found best, merge.
+					unsigned short* pa = &polys[bestPa*MAX_VERTS_PER_POLY];
+					unsigned short* pb = &polys[bestPb*MAX_VERTS_PER_POLY];
+					mergePolys(pa, pb, bestEa, bestEb);
+					memcpy(pb, &polys[(npolys-1)*MAX_VERTS_PER_POLY], sizeof(unsigned short)*MAX_VERTS_PER_POLY);
+					npolys--;
+				}
+				else
+				{
+					// Could not merge any polygons, stop.
+					break;
+				}
+			}
+		}
+		
+		// Store polygons.
+		for (int j = 0; j < npolys; ++j)
+		{
+			unsigned short* p = &mesh.polys[mesh.npolys*MAX_VERTS_PER_POLY*2];
+			unsigned short* q = &polys[j*MAX_VERTS_PER_POLY];
+			for (int k = 0; k < MAX_VERTS_PER_POLY; ++k)
+				p[k] = q[k];
+			mesh.areas[mesh.npolys] = cont.area;
+			mesh.npolys++;
+			if (mesh.npolys > maxTris)
+				return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+		}
+	}
+	
+	
+	// Remove edge vertices.
+	for (int i = 0; i < mesh.nverts; ++i)
+	{
+		if (vflags[i])
+		{
+			if (!canRemoveVertex(mesh, (unsigned short)i))
+				continue;
+			dtStatus status = removeVertex(mesh, (unsigned short)i, maxTris);
+			if (dtStatusFailed(status))
+				return status;
+			// Remove vertex
+			// Note: mesh.nverts is already decremented inside removeVertex()!
+			for (int j = i; j < mesh.nverts; ++j)
+				vflags[j] = vflags[j+1];
+			--i;
+		}
+	}
+	
+	// Calculate adjacency.
+	if (!buildMeshAdjacency(alloc, mesh.polys, mesh.npolys, mesh.verts, mesh.nverts, lcset))
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+		
+	return DT_SUCCESS;
+}
+
+dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
+							const float* pos, const float radius, const float height, const unsigned char areaId)
+{
+	float bmin[3], bmax[3];
+	bmin[0] = pos[0] - radius;
+	bmin[1] = pos[1];
+	bmin[2] = pos[2] - radius;
+	bmax[0] = pos[0] + radius;
+	bmax[1] = pos[1] + height;
+	bmax[2] = pos[2] + radius;
+	const float r2 = dtSqr(radius/cs + 0.5f);
+
+	const int w = (int)layer.header->width;
+	const int h = (int)layer.header->height;
+	const float ics = 1.0f/cs;
+	const float ich = 1.0f/ch;
+	
+	const float px = (pos[0]-orig[0])*ics;
+	const float pz = (pos[2]-orig[2])*ics;
+	
+	int minx = (int)dtMathFloorf((bmin[0]-orig[0])*ics);
+	int miny = (int)dtMathFloorf((bmin[1]-orig[1])*ich);
+	int minz = (int)dtMathFloorf((bmin[2]-orig[2])*ics);
+	int maxx = (int)dtMathFloorf((bmax[0]-orig[0])*ics);
+	int maxy = (int)dtMathFloorf((bmax[1]-orig[1])*ich);
+	int maxz = (int)dtMathFloorf((bmax[2]-orig[2])*ics);
+
+	if (maxx < 0) return DT_SUCCESS;
+	if (minx >= w) return DT_SUCCESS;
+	if (maxz < 0) return DT_SUCCESS;
+	if (minz >= h) return DT_SUCCESS;
+	
+	if (minx < 0) minx = 0;
+	if (maxx >= w) maxx = w-1;
+	if (minz < 0) minz = 0;
+	if (maxz >= h) maxz = h-1;
+	
+	for (int z = minz; z <= maxz; ++z)
+	{
+		for (int x = minx; x <= maxx; ++x)
+		{
+			const float dx = (float)(x+0.5f) - px;
+			const float dz = (float)(z+0.5f) - pz;
+			if (dx*dx + dz*dz > r2)
+				continue;
+			const int y = layer.heights[x+z*w];
+			if (y < miny || y > maxy)
+				continue;
+			layer.areas[x+z*w] = areaId;
+		}
+	}
+
+	return DT_SUCCESS;
+}
+
+
+dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
+							   dtTileCacheLayerHeader* header,
+							   const unsigned char* heights,
+							   const unsigned char* areas,
+							   const unsigned char* cons,
+							   unsigned char** outData, int* outDataSize)
+{
+	const int headerSize = dtAlign4(sizeof(dtTileCacheLayerHeader));
+	const int gridSize = (int)header->width * (int)header->height;
+	const int maxDataSize = headerSize + comp->maxCompressedSize(gridSize*3);
+	unsigned char* data = (unsigned char*)dtAlloc(maxDataSize, DT_ALLOC_PERM);
+	if (!data)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memset(data, 0, maxDataSize);
+	
+	// Store header
+	memcpy(data, header, sizeof(dtTileCacheLayerHeader));
+	
+	// Concatenate grid data for compression.
+	const int bufferSize = gridSize*3;
+	unsigned char* buffer = (unsigned char*)dtAlloc(bufferSize, DT_ALLOC_TEMP);
+	if (!buffer)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memcpy(buffer, heights, gridSize);
+	memcpy(buffer+gridSize, areas, gridSize);
+	memcpy(buffer+gridSize*2, cons, gridSize);
+	
+	// Compress
+	unsigned char* compressed = data + headerSize;
+	const int maxCompressedSize = maxDataSize - headerSize;
+	int compressedSize = 0;
+	dtStatus status = comp->compress(buffer, bufferSize, compressed, maxCompressedSize, &compressedSize);
+	if (dtStatusFailed(status))
+		return status;
+
+	*outData = data;
+	*outDataSize = headerSize + compressedSize;
+	
+	dtFree(buffer);
+	
+	return DT_SUCCESS;
+}
+
+void dtFreeTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheLayer* layer)
+{
+	dtAssert(alloc);
+	// The layer is allocated as one conitguous blob of data.
+	alloc->free(layer);
+}
+
+dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompressor* comp,
+									unsigned char* compressed, const int compressedSize,
+									dtTileCacheLayer** layerOut)
+{
+	dtAssert(alloc);
+	dtAssert(comp);
+
+	if (!layerOut)
+		return DT_FAILURE | DT_INVALID_PARAM;
+	if (!compressed)
+		return DT_FAILURE | DT_INVALID_PARAM;
+
+	*layerOut = 0;
+
+	dtTileCacheLayerHeader* compressedHeader = (dtTileCacheLayerHeader*)compressed;
+	if (compressedHeader->magic != DT_TILECACHE_MAGIC)
+		return DT_FAILURE | DT_WRONG_MAGIC;
+	if (compressedHeader->version != DT_TILECACHE_VERSION)
+		return DT_FAILURE | DT_WRONG_VERSION;
+	
+	const int layerSize = dtAlign4(sizeof(dtTileCacheLayer));
+	const int headerSize = dtAlign4(sizeof(dtTileCacheLayerHeader));
+	const int gridSize = (int)compressedHeader->width * (int)compressedHeader->height;
+	const int bufferSize = layerSize + headerSize + gridSize*4;
+	
+	unsigned char* buffer = (unsigned char*)alloc->alloc(bufferSize);
+	if (!buffer)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	memset(buffer, 0, bufferSize);
+
+	dtTileCacheLayer* layer = (dtTileCacheLayer*)buffer;
+	dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)(buffer + layerSize);
+	unsigned char* grids = buffer + layerSize + headerSize;
+	const int gridsSize = bufferSize - (layerSize + headerSize); 
+	
+	// Copy header
+	memcpy(header, compressedHeader, headerSize);
+	// Decompress grid.
+	int size = 0;
+	dtStatus status = comp->decompress(compressed+headerSize, compressedSize-headerSize,
+									   grids, gridsSize, &size);
+	if (dtStatusFailed(status))
+	{
+		dtFree(buffer);
+		return status;
+	}
+	
+	layer->header = header;
+	layer->heights = grids;
+	layer->areas = grids + gridSize;
+	layer->cons = grids + gridSize*2;
+	layer->regs = grids + gridSize*3;
+	
+	*layerOut = layer;
+	
+	return DT_SUCCESS;
+}
+
+
+
+bool dtTileCacheHeaderSwapEndian(unsigned char* data, const int dataSize)
+{
+	dtIgnoreUnused(dataSize);
+	dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)data;
+	
+	int swappedMagic = DT_TILECACHE_MAGIC;
+	int swappedVersion = DT_TILECACHE_VERSION;
+	dtSwapEndian(&swappedMagic);
+	dtSwapEndian(&swappedVersion);
+	
+	if ((header->magic != DT_TILECACHE_MAGIC || header->version != DT_TILECACHE_VERSION) &&
+		(header->magic != swappedMagic || header->version != swappedVersion))
+	{
+		return false;
+	}
+	
+	dtSwapEndian(&header->magic);
+	dtSwapEndian(&header->version);
+	dtSwapEndian(&header->tx);
+	dtSwapEndian(&header->ty);
+	dtSwapEndian(&header->tlayer);
+	dtSwapEndian(&header->bmin[0]);
+	dtSwapEndian(&header->bmin[1]);
+	dtSwapEndian(&header->bmin[2]);
+	dtSwapEndian(&header->bmax[0]);
+	dtSwapEndian(&header->bmax[1]);
+	dtSwapEndian(&header->bmax[2]);
+	dtSwapEndian(&header->hmin);
+	dtSwapEndian(&header->hmax);
+	
+	// width, height, minx, maxx, miny, maxy are unsigned char, no need to swap.
+	
+	return true;
+}
+

+ 546 - 0
Source/ThirdParty/STB/stb_rect_pack.h

@@ -0,0 +1,546 @@
+// stb_rect_pack.h - v0.05 - public domain - rectangle packing
+// Sean Barrett 2014
+//
+// Useful for e.g. packing rectangular textures into an atlas.
+// Does not do rotation.
+//
+// Not necessarily the awesomest packing method, but better than
+// the totally naive one in stb_truetype (which is primarily what
+// this is meant to replace).
+//
+// Has only had a few tests run, may have issues.
+//
+// More docs to come.
+//
+// No memory allocations; uses qsort() and assert() from stdlib.
+//
+// This library currently uses the Skyline Bottom-Left algorithm.
+//
+// Please note: better rectangle packers are welcome! Please
+// implement them to the same API, but with a different init
+// function.
+//
+// Version history:
+//
+//     0.05:  added STBRP_ASSERT to allow replacing assert
+//     0.04:  fixed minor bug in STBRP_LARGE_RECTS support
+//     0.01:  initial release
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//       INCLUDE SECTION
+//
+
+#ifndef STB_INCLUDE_STB_RECT_PACK_H
+#define STB_INCLUDE_STB_RECT_PACK_H
+
+#define STB_RECT_PACK_VERSION  1
+
+#ifdef STBRP_STATIC
+#define STBRP_DEF static
+#else
+#define STBRP_DEF extern
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct stbrp_context stbrp_context;
+typedef struct stbrp_node    stbrp_node;
+typedef struct stbrp_rect    stbrp_rect;
+
+#ifdef STBRP_LARGE_RECTS
+typedef int            stbrp_coord;
+#else
+typedef unsigned short stbrp_coord;
+#endif
+
+STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
+// Assign packed locations to rectangles. The rectangles are of type
+// 'stbrp_rect' defined below, stored in the array 'rects', and there
+// are 'num_rects' many of them.
+//
+// Rectangles which are successfully packed have the 'was_packed' flag
+// set to a non-zero value and 'x' and 'y' store the minimum location
+// on each axis (i.e. bottom-left in cartesian coordinates, top-left
+// if you imagine y increasing downwards). Rectangles which do not fit
+// have the 'was_packed' flag set to 0.
+//
+// You should not try to access the 'rects' array from another thread
+// while this function is running, as the function temporarily reorders
+// the array while it executes.
+//
+// To pack into another rectangle, you need to call stbrp_init_target
+// again. To continue packing into the same rectangle, you can call
+// this function again. Calling this multiple times with multiple rect
+// arrays will probably produce worse packing results than calling it
+// a single time with the full rectangle array, but the option is
+// available.
+
+struct stbrp_rect
+{
+   // reserved for your use:
+   int            id;
+
+   // input:
+   stbrp_coord    w, h;
+
+   // output:
+   stbrp_coord    x, y;
+   int            was_packed;  // non-zero if valid packing
+
+}; // 16 bytes, nominally
+
+
+STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
+// Initialize a rectangle packer to:
+//    pack a rectangle that is 'width' by 'height' in dimensions
+//    using temporary storage provided by the array 'nodes', which is 'num_nodes' long
+//
+// You must call this function every time you start packing into a new target.
+//
+// There is no "shutdown" function. The 'nodes' memory must stay valid for
+// the following stbrp_pack_rects() call (or calls), but can be freed after
+// the call (or calls) finish.
+//
+// Note: to guarantee best results, either:
+//       1. make sure 'num_nodes' >= 'width'
+//   or  2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
+//
+// If you don't do either of the above things, widths will be quantized to multiples
+// of small integers to guarantee the algorithm doesn't run out of temporary storage.
+//
+// If you do #2, then the non-quantized algorithm will be used, but the algorithm
+// may run out of temporary storage and be unable to pack some rectangles.
+
+STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
+// Optionally call this function after init but before doing any packing to
+// change the handling of the out-of-temp-memory scenario, described above.
+// If you call init again, this will be reset to the default (false).
+
+
+STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
+// Optionally select which packing heuristic the library should use. Different
+// heuristics will produce better/worse results for different data sets.
+// If you call init again, this will be reset to the default.
+
+enum
+{
+   STBRP_HEURISTIC_Skyline_default=0,
+   STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
+   STBRP_HEURISTIC_Skyline_BF_sortHeight,
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// the details of the following structures don't matter to you, but they must
+// be visible so you can handle the memory allocations for them
+
+struct stbrp_node
+{
+   stbrp_coord  x,y;
+   stbrp_node  *next;
+};
+
+struct stbrp_context
+{
+   int width;
+   int height;
+   int align;
+   int init_mode;
+   int heuristic;
+   int num_nodes;
+   stbrp_node *active_head;
+   stbrp_node *free_head;
+   stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//     IMPLEMENTATION SECTION
+//
+
+#ifdef STB_RECT_PACK_IMPLEMENTATION
+#include <stdlib.h>
+
+#ifndef STBRP_ASSERT
+#include <assert.h>
+#define STBRP_ASSERT assert
+#endif
+
+enum
+{
+   STBRP__INIT_skyline = 1,
+};
+
+STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
+{
+   switch (context->init_mode) {
+      case STBRP__INIT_skyline:
+         STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
+         context->heuristic = heuristic;
+         break;
+      default:
+         STBRP_ASSERT(0);
+   }
+}
+
+STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
+{
+   if (allow_out_of_mem)
+      // if it's ok to run out of memory, then don't bother aligning them;
+      // this gives better packing, but may fail due to OOM (even though
+      // the rectangles easily fit). @TODO a smarter approach would be to only
+      // quantize once we've hit OOM, then we could get rid of this parameter.
+      context->align = 1;
+   else {
+      // if it's not ok to run out of memory, then quantize the widths
+      // so that num_nodes is always enough nodes.
+      //
+      // I.e. num_nodes * align >= width
+      //                  align >= width / num_nodes
+      //                  align = ceil(width/num_nodes)
+
+      context->align = (context->width + context->num_nodes-1) / context->num_nodes;
+   }
+}
+
+STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
+{
+   int i;
+#ifndef STBRP_LARGE_RECTS
+   STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
+#endif
+
+   for (i=0; i < num_nodes-1; ++i)
+      nodes[i].next = &nodes[i+1];
+   nodes[i].next = NULL;
+   context->init_mode = STBRP__INIT_skyline;
+   context->heuristic = STBRP_HEURISTIC_Skyline_default;
+   context->free_head = &nodes[0];
+   context->active_head = &context->extra[0];
+   context->width = width;
+   context->height = height;
+   context->num_nodes = num_nodes;
+   stbrp_setup_allow_out_of_mem(context, 0);
+
+   // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
+   context->extra[0].x = 0;
+   context->extra[0].y = 0;
+   context->extra[0].next = &context->extra[1];
+   context->extra[1].x = (stbrp_coord) width;
+#ifdef STBRP_LARGE_RECTS
+   context->extra[1].y = (1<<30);
+#else
+   context->extra[1].y = 65535;
+#endif
+   context->extra[1].next = NULL;
+}
+
+// find minimum y position if it starts at x1
+static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
+{
+   stbrp_node *node = first;
+   int x1 = x0 + width;
+   int min_y, visited_width, waste_area;
+   STBRP_ASSERT(first->x <= x0);
+
+   #if 0
+   // skip in case we're past the node
+   while (node->next->x <= x0)
+      ++node;
+   #else
+   STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
+   #endif
+
+   STBRP_ASSERT(node->x <= x0);
+
+   min_y = 0;
+   waste_area = 0;
+   visited_width = 0;
+   while (node->x < x1) {
+      if (node->y > min_y) {
+         // raise min_y higher.
+         // we've accounted for all waste up to min_y,
+         // but we'll now add more waste for everything we've visted
+         waste_area += visited_width * (node->y - min_y);
+         min_y = node->y;
+         // the first time through, visited_width might be reduced
+         if (node->x < x0)
+            visited_width += node->next->x - x0;
+         else
+            visited_width += node->next->x - node->x;
+      } else {
+         // add waste area
+         int under_width = node->next->x - node->x;
+         if (under_width + visited_width > width)
+            under_width = width - visited_width;
+         waste_area += under_width * (min_y - node->y);
+         visited_width += under_width;
+      }
+      node = node->next;
+   }
+
+   *pwaste = waste_area;
+   return min_y;
+}
+
+typedef struct
+{
+   int x,y;
+   stbrp_node **prev_link;
+} stbrp__findresult;
+
+static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
+{
+   int best_waste = (1<<30), best_x, best_y = (1 << 30);
+   stbrp__findresult fr;
+   stbrp_node **prev, *node, *tail, **best = NULL;
+
+   // align to multiple of c->align
+   width = (width + c->align - 1);
+   width -= width % c->align;
+   STBRP_ASSERT(width % c->align == 0);
+
+   node = c->active_head;
+   prev = &c->active_head;
+   while (node->x + width <= c->width) {
+      int y,waste;
+      y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
+      if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
+         // bottom left
+         if (y < best_y) {
+            best_y = y;
+            best = prev;
+         }
+      } else {
+         // best-fit
+         if (y + height <= c->height) {
+            // can only use it if it first vertically
+            if (y < best_y || (y == best_y && waste < best_waste)) {
+               best_y = y;
+               best_waste = waste;
+               best = prev;
+            }
+         }
+      }
+      prev = &node->next;
+      node = node->next;
+   }
+
+   best_x = (best == NULL) ? 0 : (*best)->x;
+
+   // if doing best-fit (BF), we also have to try aligning right edge to each node position
+   //
+   // e.g, if fitting
+   //
+   //     ____________________
+   //    |____________________|
+   //
+   //            into
+   //
+   //   |                         |
+   //   |             ____________|
+   //   |____________|
+   //
+   // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
+   //
+   // This makes BF take about 2x the time
+
+   if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
+      tail = c->active_head;
+      node = c->active_head;
+      prev = &c->active_head;
+      // find first node that's admissible
+      while (tail->x < width)
+         tail = tail->next;
+      while (tail) {
+         int xpos = tail->x - width;
+         int y,waste;
+         STBRP_ASSERT(xpos >= 0);
+         // find the left position that matches this
+         while (node->next->x <= xpos) {
+            prev = &node->next;
+            node = node->next;
+         }
+         STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
+         y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
+         if (y + height < c->height) {
+            if (y <= best_y) {
+               if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
+                  best_x = xpos;
+                  STBRP_ASSERT(y <= best_y);
+                  best_y = y;
+                  best_waste = waste;
+                  best = prev;
+               }
+            }
+         }
+         tail = tail->next;
+      }         
+   }
+
+   fr.prev_link = best;
+   fr.x = best_x;
+   fr.y = best_y;
+   return fr;
+}
+
+static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
+{
+   // find best position according to heuristic
+   stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
+   stbrp_node *node, *cur;
+
+   // bail if:
+   //    1. it failed
+   //    2. the best node doesn't fit (we don't always check this)
+   //    3. we're out of memory
+   if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
+      res.prev_link = NULL;
+      return res;
+   }
+
+   // on success, create new node
+   node = context->free_head;
+   node->x = (stbrp_coord) res.x;
+   node->y = (stbrp_coord) (res.y + height);
+
+   context->free_head = node->next;
+
+   // insert the new node into the right starting point, and
+   // let 'cur' point to the remaining nodes needing to be
+   // stiched back in
+
+   cur = *res.prev_link;
+   if (cur->x < res.x) {
+      // preserve the existing one, so start testing with the next one
+      stbrp_node *next = cur->next;
+      cur->next = node;
+      cur = next;
+   } else {
+      *res.prev_link = node;
+   }
+
+   // from here, traverse cur and free the nodes, until we get to one
+   // that shouldn't be freed
+   while (cur->next && cur->next->x <= res.x + width) {
+      stbrp_node *next = cur->next;
+      // move the current node to the free list
+      cur->next = context->free_head;
+      context->free_head = cur;
+      cur = next;
+   }
+
+   // stitch the list back in
+   node->next = cur;
+
+   if (cur->x < res.x + width)
+      cur->x = (stbrp_coord) (res.x + width);
+
+#ifdef _DEBUG
+   cur = context->active_head;
+   while (cur->x < context->width) {
+      STBRP_ASSERT(cur->x < cur->next->x);
+      cur = cur->next;
+   }
+   STBRP_ASSERT(cur->next == NULL);
+
+   {
+      stbrp_node *L1 = NULL, *L2 = NULL;
+      int count=0;
+      cur = context->active_head;
+      while (cur) {
+         L1 = cur;
+         cur = cur->next;
+         ++count;
+      }
+      cur = context->free_head;
+      while (cur) {
+         L2 = cur;
+         cur = cur->next;
+         ++count;
+      }
+      STBRP_ASSERT(count == context->num_nodes+2);
+   }
+#endif
+
+   return res;
+}
+
+static int rect_height_compare(const void *a, const void *b)
+{
+   stbrp_rect *p = (stbrp_rect *) a;
+   stbrp_rect *q = (stbrp_rect *) b;
+   if (p->h > q->h)
+      return -1;
+   if (p->h < q->h)
+      return  1;
+   return (p->w > q->w) ? -1 : (p->w < q->w);
+}
+
+static int rect_width_compare(const void *a, const void *b)
+{
+   stbrp_rect *p = (stbrp_rect *) a;
+   stbrp_rect *q = (stbrp_rect *) b;
+   if (p->w > q->w)
+      return -1;
+   if (p->w < q->w)
+      return  1;
+   return (p->h > q->h) ? -1 : (p->h < q->h);
+}
+
+static int rect_original_order(const void *a, const void *b)
+{
+   stbrp_rect *p = (stbrp_rect *) a;
+   stbrp_rect *q = (stbrp_rect *) b;
+   return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
+}
+
+#ifdef STBRP_LARGE_RECTS
+#define STBRP__MAXVAL  0xffffffff
+#else
+#define STBRP__MAXVAL  0xffff
+#endif
+
+STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
+{
+   int i;
+
+   // we use the 'was_packed' field internally to allow sorting/unsorting
+   for (i=0; i < num_rects; ++i) {
+      rects[i].was_packed = i;
+      #ifndef STBRP_LARGE_RECTS
+      STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
+      #endif
+   }
+
+   // sort according to heuristic
+   qsort(rects, num_rects, sizeof(rects[0]), rect_height_compare);
+
+   for (i=0; i < num_rects; ++i) {
+      stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
+      if (fr.prev_link) {
+         rects[i].x = (stbrp_coord) fr.x;
+         rects[i].y = (stbrp_coord) fr.y;
+      } else {
+         rects[i].x = rects[i].y = STBRP__MAXVAL;
+      }
+   }
+
+   // unsort
+   qsort(rects, num_rects, sizeof(rects[0]), rect_original_order);
+
+   // set was_packed flags
+   for (i=0; i < num_rects; ++i)
+      rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
+}
+#endif