Browse Source

Merge pull request #298 from AtomicGameEngine/JME-ATOMIC-UPDATE

UPDATE
JoshEngebretson 10 years ago
parent
commit
b59c2050ad
100 changed files with 3559 additions and 1611 deletions
  1. 24 6
      AUTHORS.md
  2. 1 1
      CMake/Modules/AtomicLinux.cmake
  3. 2 2
      Resources/CoreData/RenderPaths/ForwardDepth.xml
  4. 11 9
      Resources/CoreData/Shaders/HLSL/Basic.hlsl
  5. 3 3
      Resources/CoreData/Shaders/HLSL/Depth.hlsl
  6. 9 6
      Resources/CoreData/Shaders/HLSL/Samplers.hlsl
  7. 3 0
      Resources/CoreData/Techniques/BasicVColUnlitAlpha.xml
  8. 20 4
      Source/Atomic/Atomic3D/AnimatedModel.cpp
  9. 2 0
      Source/Atomic/Atomic3D/AnimatedModel.h
  10. 29 3
      Source/Atomic/Atomic3D/AnimationController.cpp
  11. 8 1
      Source/Atomic/Atomic3D/AnimationController.h
  12. 4 0
      Source/Atomic/Atomic3D/DecalSet.cpp
  13. 84 8
      Source/Atomic/Atomic3D/Terrain.cpp
  14. 19 1
      Source/Atomic/Atomic3D/Terrain.h
  15. 11 22
      Source/Atomic/Atomic3D/TerrainPatch.cpp
  16. 5 12
      Source/Atomic/Atomic3D/TerrainPatch.h
  17. 4 0
      Source/Atomic/Audio/Audio.cpp
  18. 12 8
      Source/Atomic/Container/Str.cpp
  19. 3 3
      Source/Atomic/Container/Str.h
  20. 19 0
      Source/Atomic/Container/Vector.h
  21. 1 1
      Source/Atomic/Core/Attribute.h
  22. 3 5
      Source/Atomic/Core/ProcessUtils.cpp
  23. 93 0
      Source/Atomic/Core/Spline.cpp
  24. 31 20
      Source/Atomic/Core/Spline.h
  25. 73 18
      Source/Atomic/Core/Variant.cpp
  26. 120 23
      Source/Atomic/Core/Variant.h
  27. 5 1
      Source/Atomic/Core/WorkQueue.cpp
  28. 4 0
      Source/Atomic/Core/WorkQueue.h
  29. 99 0
      Source/Atomic/Database/Database.cpp
  30. 74 0
      Source/Atomic/Database/Database.h
  31. 44 0
      Source/Atomic/Database/DatabaseEvents.h
  32. 31 0
      Source/Atomic/Database/DbConnection.h
  33. 31 0
      Source/Atomic/Database/DbResult.h
  34. 158 0
      Source/Atomic/Database/ODBC/ODBCConnection.cpp
  35. 68 0
      Source/Atomic/Database/ODBC/ODBCConnection.h
  36. 73 0
      Source/Atomic/Database/ODBC/ODBCResult.h
  37. 162 0
      Source/Atomic/Database/SQLite/SQLiteConnection.cpp
  38. 65 0
      Source/Atomic/Database/SQLite/SQLiteConnection.h
  39. 68 0
      Source/Atomic/Database/SQLite/SQLiteResult.h
  40. 0 3
      Source/Atomic/Engine/Application.cpp
  41. 9 0
      Source/Atomic/Engine/Engine.cpp
  42. 17 5
      Source/Atomic/Graphics/Batch.cpp
  43. 18 12
      Source/Atomic/Graphics/Batch.h
  44. 66 35
      Source/Atomic/Graphics/Direct3D11/D3D11Graphics.cpp
  45. 3 1
      Source/Atomic/Graphics/Direct3D11/D3D11Graphics.h
  46. 2 1
      Source/Atomic/Graphics/Direct3D11/D3D11GraphicsImpl.cpp
  47. 2 0
      Source/Atomic/Graphics/Direct3D11/D3D11GraphicsImpl.h
  48. 1 1
      Source/Atomic/Graphics/Direct3D11/D3D11ShaderVariation.cpp
  49. 68 54
      Source/Atomic/Graphics/Direct3D11/D3D11TextureCube.cpp
  50. 67 56
      Source/Atomic/Graphics/Direct3D9/D3D9TextureCube.cpp
  51. 4 0
      Source/Atomic/Graphics/Drawable.cpp
  52. 15 0
      Source/Atomic/Graphics/Material.cpp
  53. 9 0
      Source/Atomic/Graphics/Material.h
  54. 2 1
      Source/Atomic/Graphics/OcclusionBuffer.cpp
  55. 3 2
      Source/Atomic/Graphics/Octree.cpp
  56. 5 5
      Source/Atomic/Graphics/OpenGL/OGLGraphics.cpp
  57. 1 1
      Source/Atomic/Graphics/OpenGL/OGLGraphics.h
  58. 68 54
      Source/Atomic/Graphics/OpenGL/OGLTextureCube.cpp
  59. 4 0
      Source/Atomic/Graphics/RenderPath.cpp
  60. 4 0
      Source/Atomic/Graphics/Renderer.cpp
  61. 1 1
      Source/Atomic/Graphics/Renderer.h
  62. 1 1
      Source/Atomic/Graphics/Shader.cpp
  63. 37 61
      Source/Atomic/Graphics/View.cpp
  64. 4 3
      Source/Atomic/Graphics/View.h
  65. 12 1
      Source/Atomic/IO/Deserializer.cpp
  66. 2 0
      Source/Atomic/IO/Deserializer.h
  67. 3 2
      Source/Atomic/IO/File.cpp
  68. 11 0
      Source/Atomic/IO/File.h
  69. 74 79
      Source/Atomic/IO/FileSystem.cpp
  70. 2 2
      Source/Atomic/IO/PackageFile.cpp
  71. 13 1
      Source/Atomic/IO/Serializer.cpp
  72. 2 0
      Source/Atomic/IO/Serializer.h
  73. 2 2
      Source/Atomic/Input/Input.cpp
  74. 7 2
      Source/Atomic/Math/AreaAllocator.cpp
  75. 1 1
      Source/Atomic/Math/AreaAllocator.h
  76. 2 0
      Source/Atomic/Math/BoundingBox.cpp
  77. 2 0
      Source/Atomic/Math/Color.cpp
  78. 13 0
      Source/Atomic/Math/Color.h
  79. 2 0
      Source/Atomic/Math/Frustum.cpp
  80. 48 2
      Source/Atomic/Math/MathDefs.h
  81. 2 0
      Source/Atomic/Math/Plane.cpp
  82. 9 0
      Source/Atomic/Math/Plane.h
  83. 6 0
      Source/Atomic/Math/Polyhedron.cpp
  84. 7 0
      Source/Atomic/Math/Polyhedron.h
  85. 14 5
      Source/Atomic/Math/Quaternion.cpp
  86. 2 0
      Source/Atomic/Math/Random.cpp
  87. 2 0
      Source/Atomic/Math/Ray.cpp
  88. 2 0
      Source/Atomic/Math/Rect.cpp
  89. 2 0
      Source/Atomic/Math/Sphere.cpp
  90. 8 0
      Source/Atomic/Math/Vector2.h
  91. 311 198
      Source/Atomic/Navigation/CrowdAgent.cpp
  92. 110 57
      Source/Atomic/Navigation/CrowdAgent.h
  93. 690 0
      Source/Atomic/Navigation/CrowdManager.cpp
  94. 198 0
      Source/Atomic/Navigation/CrowdManager.h
  95. 0 546
      Source/Atomic/Navigation/DetourCrowdManager.cpp
  96. 0 146
      Source/Atomic/Navigation/DetourCrowdManager.h
  97. 34 38
      Source/Atomic/Navigation/DynamicNavigationMesh.cpp
  98. 3 0
      Source/Atomic/Navigation/DynamicNavigationMesh.h
  99. 43 44
      Source/Atomic/Navigation/NavArea.cpp
  100. 33 31
      Source/Atomic/Navigation/NavArea.h

+ 24 - 6
AUTHORS.md

@@ -25,12 +25,14 @@ Atomic Game Engine contribution copyrights are held by their authors.  Each auth
 
 
 The Atomic Game Engine began as a fork of the Urho3D project in November 2014
 The Atomic Game Engine began as a fork of the Urho3D project in November 2014
 
 
-Website:
+### Urho3D Website
 
 
-http://urho3d.github.io/
+Main website: [http://urho3d.github.io/](http://urho3d.github.io/)
 
 
-Credits:
+##License
+Licensed under the MIT license, see [License.txt](https://github.com/urho3d/Urho3D/blob/master/License.txt) for details.
 
 
+##Credits
 Urho3D development, contributions and bugfixes by:
 Urho3D development, contributions and bugfixes by:
 - Lasse Öörni ([email protected], AgentC at GameDev.net)
 - Lasse Öörni ([email protected], AgentC at GameDev.net)
 - Wei Tjong Yao
 - Wei Tjong Yao
@@ -40,6 +42,7 @@ Urho3D development, contributions and bugfixes by:
 - Danny Boisvert
 - Danny Boisvert
 - Carlo Carollo
 - Carlo Carollo
 - Pete Chown
 - Pete Chown
+- Christian Clavet
 - Sebastian Delatorre (primitivewaste)
 - Sebastian Delatorre (primitivewaste)
 - Josh Engebretson
 - Josh Engebretson
 - Chris Friesen
 - Chris Friesen
@@ -50,6 +53,7 @@ Urho3D development, contributions and bugfixes by:
 - Gunnar Kriik
 - Gunnar Kriik
 - Ali Kämäräinen
 - Ali Kämäräinen
 - Pete Leigh
 - Pete Leigh
+- Thorbjørn Lindeijer
 - Jonne Nauha
 - Jonne Nauha
 - Paul Noome
 - Paul Noome
 - David Palacios
 - David Palacios
@@ -57,7 +61,9 @@ Urho3D development, contributions and bugfixes by:
 - Jordan Patterson
 - Jordan Patterson
 - Vladimir Pobedinsky
 - Vladimir Pobedinsky
 - Nick Royer
 - Nick Royer
+- Jonathan Sandusky
 - Miika Santala
 - Miika Santala
+- James Thomas
 - Joshua Tippetts
 - Joshua Tippetts
 - Daniel Wiberg
 - Daniel Wiberg
 - Steven Zhang
 - Steven Zhang
@@ -66,22 +72,31 @@ Urho3D development, contributions and bugfixes by:
 - Firegorilla
 - Firegorilla
 - Magic.Lixin
 - Magic.Lixin
 - Mike3D
 - Mike3D
+- Modanung
+- MonkeyFirst
 - OvermindDL1
 - OvermindDL1
+- Skrylar
+- 1vanK
 - andmar1x
 - andmar1x
 - amadeus_osa
 - amadeus_osa
 - atship
 - atship
 - att
 - att
 - celeron55
 - celeron55
+- cosmy1
+- feltech
 - hdunderscore
 - hdunderscore
+- marynate
 - mightyCelu
 - mightyCelu
 - nemerle
 - nemerle
 - ninjastone
 - ninjastone
+- raould
 - rasteron
 - rasteron
 - reattiva
 - reattiva
 - rifai
 - rifai
 - skaiware
 - skaiware
 - szamq
 - szamq
 - thebluefish
 - thebluefish
+- yushli
 
 
 Urho3D is greatly inspired by OGRE (http://www.ogre3d.org) and Horde3D
 Urho3D is greatly inspired by OGRE (http://www.ogre3d.org) and Horde3D
 (http://www.horde3d.org). Additional inspiration & research used:
 (http://www.horde3d.org). Additional inspiration & research used:
@@ -119,15 +134,18 @@ Urho3D uses the following third-party libraries:
 - LuaJIT 2.0.3 (http://www.luajit.org)
 - LuaJIT 2.0.3 (http://www.luajit.org)
 - LZ4 (http://code.google.com/p/lz4/)
 - LZ4 (http://code.google.com/p/lz4/)
 - MojoShader (http://icculus.org/mojoshader/)
 - MojoShader (http://icculus.org/mojoshader/)
+- nanodbc 2.2.2 (http://lexicalunit.github.io/nanodbc/)
 - Open Asset Import Library (http://assimp.sourceforge.net/)
 - Open Asset Import Library (http://assimp.sourceforge.net/)
-- pugixml 1.0 (http://pugixml.org/)
+- pugixml 1.5 (http://pugixml.org/)
 - rapidjson 0.11 (https://code.google.com/p/rapidjson/)
 - rapidjson 0.11 (https://code.google.com/p/rapidjson/)
 - Recast/Detour (https://github.com/memononen/recastnavigation/)
 - Recast/Detour (https://github.com/memononen/recastnavigation/)
 - SDL 2.0.3 (http://www.libsdl.org/)
 - SDL 2.0.3 (http://www.libsdl.org/)
 - StanHull (http://codesuppository.blogspot.com/2006/03/
 - StanHull (http://codesuppository.blogspot.com/2006/03/
   john-ratcliffs-code-suppository-blog.html)
   john-ratcliffs-code-suppository-blog.html)
-- stb_image 1.29 (http://nothings.org/)
-- stb_vorbis 0.99996 (http://nothings.org/)
+- stb_image 2.05 (http://nothings.org/)
+- stb_image_write 0.98 (http://nothings.org/)
+- stb_vorbis 1.05 (http://nothings.org/)
+- SQLite 3.8.10.2 (https://www.sqlite.org/)
 - tolua++ 1.0.93 (http://www.codenix.com/~tolua)
 - tolua++ 1.0.93 (http://www.codenix.com/~tolua)
 
 
 DXT / ETC1 / PVRTC decompression code based on the Squish library and the Oolong
 DXT / ETC1 / PVRTC decompression code based on the Squish library and the Oolong

+ 1 - 1
CMake/Modules/AtomicLinux.cmake

@@ -14,5 +14,5 @@ pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
 list (APPEND ATOMIC_LINK_LIBRARIES pthread GLEW GL dl)
 list (APPEND ATOMIC_LINK_LIBRARIES pthread GLEW GL dl)
 
 
 
 
-add_definitions(-DATOMIC_PLATFORM_WEB)
+add_definitions(-DATOMIC_PLATFORM_LINUX)
 add_definitions(-DATOMIC_OPENGL -Wno-warn-absolute-paths -DATOMIC_TBUI)
 add_definitions(-DATOMIC_OPENGL -Wno-warn-absolute-paths -DATOMIC_TBUI)

+ 2 - 2
Resources/CoreData/RenderPaths/ForwardDepth.xml

@@ -1,8 +1,8 @@
 <renderpath>
 <renderpath>
     <rendertarget name="depth" sizedivisor="1 1" format="lineardepth" />
     <rendertarget name="depth" sizedivisor="1 1" format="lineardepth" />
-    <command type="clear" color="1 1 1 1" depth="1.0" output="depth" />
+    <command type="clear" color="1 1 1 1" depth="1.0" stencil="0" output="depth" />
     <command type="scenepass" pass="depth" output="depth" />
     <command type="scenepass" pass="depth" output="depth" />
-    <command type="clear" color="fog" depth="1.0" stencil="0" />
+    <command type="clear" color="fog" />
     <command type="scenepass" pass="base" vertexlights="true" metadata="base" />
     <command type="scenepass" pass="base" vertexlights="true" metadata="base" />
     <command type="forwardlights" pass="light" />
     <command type="forwardlights" pass="light" />
     <command type="scenepass" pass="postopaque" />
     <command type="scenepass" pass="postopaque" />

+ 11 - 9
Resources/CoreData/Shaders/HLSL/Basic.hlsl

@@ -3,6 +3,12 @@
 #include "Transform.hlsl"
 #include "Transform.hlsl"
 
 
 void VS(float4 iPos : POSITION,
 void VS(float4 iPos : POSITION,
+    #ifdef DIFFMAP
+        float2 iTexCoord : TEXCOORD0,
+    #endif
+    #ifdef VERTEXCOLOR
+        float4 iColor : COLOR0,
+    #endif
     #ifdef SKINNED
     #ifdef SKINNED
         float4 iBlendWeights : BLENDWEIGHT,
         float4 iBlendWeights : BLENDWEIGHT,
         int4 iBlendIndices : BLENDINDICES,
         int4 iBlendIndices : BLENDINDICES,
@@ -14,15 +20,11 @@ void VS(float4 iPos : POSITION,
         float2 iSize : TEXCOORD1,
         float2 iSize : TEXCOORD1,
     #endif
     #endif
     #ifdef DIFFMAP
     #ifdef DIFFMAP
-        float2 iTexCoord : TEXCOORD0,
+        out float2 oTexCoord : TEXCOORD0,
     #endif
     #endif
     #ifdef VERTEXCOLOR
     #ifdef VERTEXCOLOR
-        float4 iColor : COLOR0,
         out float4 oColor : COLOR0,
         out float4 oColor : COLOR0,
     #endif
     #endif
-    #ifdef DIFFMAP
-        out float2 oTexCoord : TEXCOORD0,
-    #endif
     #if defined(D3D11) && defined(CLIPPLANE)
     #if defined(D3D11) && defined(CLIPPLANE)
         out float oClip : SV_CLIPDISTANCE0,
         out float oClip : SV_CLIPDISTANCE0,
     #endif
     #endif
@@ -45,15 +47,15 @@ void VS(float4 iPos : POSITION,
 }
 }
 
 
 void PS(
 void PS(
-    #ifdef VERTEXCOLOR
-        float4 iColor : COLOR0,
-    #endif
     #if defined(DIFFMAP) || defined(ALPHAMAP)
     #if defined(DIFFMAP) || defined(ALPHAMAP)
         float2 iTexCoord : TEXCOORD0,
         float2 iTexCoord : TEXCOORD0,
     #endif
     #endif
+    #ifdef VERTEXCOLOR
+        float4 iColor : COLOR0,
+    #endif
     #if defined(D3D11) && defined(CLIPPLANE)
     #if defined(D3D11) && defined(CLIPPLANE)
         float iClip : SV_CLIPDISTANCE0,
         float iClip : SV_CLIPDISTANCE0,
-    #endif    
+    #endif
     out float4 oColor : OUTCOLOR0)
     out float4 oColor : OUTCOLOR0)
 {
 {
     float4 diffColor = cMatDiffColor;
     float4 diffColor = cMatDiffColor;

+ 3 - 3
Resources/CoreData/Shaders/HLSL/Depth.hlsl

@@ -12,7 +12,7 @@ void VS(float4 iPos : POSITION,
     #endif
     #endif
     float2 iTexCoord : TEXCOORD0,
     float2 iTexCoord : TEXCOORD0,
     out float3 oTexCoord : TEXCOORD0,
     out float3 oTexCoord : TEXCOORD0,
-    out float4 oPos : POSITION)
+    out float4 oPos : OUTPOSITION)
 {
 {
     float4x3 modelMatrix = iModelMatrix;
     float4x3 modelMatrix = iModelMatrix;
     float3 worldPos = GetWorldPos(modelMatrix);
     float3 worldPos = GetWorldPos(modelMatrix);
@@ -22,10 +22,10 @@ void VS(float4 iPos : POSITION,
 
 
 void PS(
 void PS(
     float3 iTexCoord : TEXCOORD0,
     float3 iTexCoord : TEXCOORD0,
-    out float4 oColor : COLOR0)
+    out float4 oColor : OUTCOLOR0)
 {
 {
     #ifdef ALPHAMASK
     #ifdef ALPHAMASK
-        float alpha = tex2D(sDiffMap, iTexCoord.xy).a;
+        float alpha = Sample2D(sDiffMap, iTexCoord.xy).a;
         if (alpha < 0.5)
         if (alpha < 0.5)
             discard;
             discard;
     #endif
     #endif

+ 9 - 6
Resources/CoreData/Shaders/HLSL/Samplers.hlsl

@@ -1,3 +1,12 @@
+#ifdef D3D11
+// Make sampling macros also available for VS on D3D11
+#define Sample2D(tex, uv) t##tex.Sample(s##tex, uv)
+#define Sample2DProj(tex, uv) t##tex.Sample(s##tex, uv.xy / uv.w)
+#define Sample2DLod0(tex, uv) t##tex.SampleLevel(s##tex, uv, 0.0)
+#define SampleCube(tex, uv) t##tex.Sample(s##tex, uv)
+#define SampleShadow(tex, uv) t##tex.SampleCmpLevelZero(s##tex, uv.xy, uv.z)
+#endif
+
 #ifdef COMPILEPS
 #ifdef COMPILEPS
 
 
 #ifndef D3D11
 #ifndef D3D11
@@ -76,12 +85,6 @@ SamplerState sLightBuffer : register(s14);
 SamplerState sZoneCubeMap : register(s15);
 SamplerState sZoneCubeMap : register(s15);
 SamplerState sZoneVolumeMap : register(s15);
 SamplerState sZoneVolumeMap : register(s15);
 
 
-#define Sample2D(tex, uv) t##tex.Sample(s##tex, uv)
-#define Sample2DProj(tex, uv) t##tex.Sample(s##tex, uv.xy / uv.w)
-#define Sample2DLod0(tex, uv) t##tex.SampleLevel(s##tex, uv, 0.0)
-#define SampleCube(tex, uv) t##tex.Sample(s##tex, uv)
-#define SampleShadow(tex, uv) t##tex.SampleCmpLevelZero(s##tex, uv.xy, uv.z)
-
 #endif
 #endif
 
 
 float3 DecodeNormal(float4 normalInput)
 float3 DecodeNormal(float4 normalInput)

+ 3 - 0
Resources/CoreData/Techniques/BasicVColUnlitAlpha.xml

@@ -0,0 +1,3 @@
+<technique vs="Basic" ps="Basic" vsdefines="DIFFMAP VERTEXCOLOR" psdefines="DIFFMAP VERTEXCOLOR">
+    <pass name="alpha" depthwrite="false" blend="alpha" />
+</technique>

+ 20 - 4
Source/Atomic/Atomic3D/AnimatedModel.cpp

@@ -73,7 +73,8 @@ AnimatedModel::AnimatedModel(Context* context) :
     boneBoundingBoxDirty_(true),
     boneBoundingBoxDirty_(true),
     isMaster_(true),
     isMaster_(true),
     loading_(false),
     loading_(false),
-    assignBonesPending_(false)
+    assignBonesPending_(false),
+    forceAnimationUpdate_(false)
 {
 {
 }
 }
 
 
@@ -211,9 +212,17 @@ void AnimatedModel::Update(const FrameInfo& frame)
     // If headless, retain the current animation distance (should be 0)
     // If headless, retain the current animation distance (should be 0)
     if (frame.camera_ && abs((int)frame.frameNumber_ - (int)viewFrameNumber_) > 1)
     if (frame.camera_ && abs((int)frame.frameNumber_ - (int)viewFrameNumber_) > 1)
     {
     {
-        // First check for no update at all when invisible
+        // First check for no update at all when invisible. In that case reset LOD timer to ensure update
+        // next time the model is in view
         if (!updateInvisible_)
         if (!updateInvisible_)
+        {
+            if (animationDirty_)
+            {
+                animationLodTimer_ = -1.0f;
+                forceAnimationUpdate_ = true;
+            }
             return;
             return;
+        }
         float distance = frame.camera_->GetDistance(node_->GetWorldPosition());
         float distance = frame.camera_->GetDistance(node_->GetWorldPosition());
         // If distance is greater than draw distance, no need to update at all
         // If distance is greater than draw distance, no need to update at all
         if (drawDistance_ > 0.0f && distance > drawDistance_)
         if (drawDistance_ > 0.0f && distance > drawDistance_)
@@ -268,6 +277,13 @@ void AnimatedModel::UpdateBatches(const FrameInfo& frame)
 
 
 void AnimatedModel::UpdateGeometry(const FrameInfo& frame)
 void AnimatedModel::UpdateGeometry(const FrameInfo& frame)
 {
 {
+    // Late update in case the model came into view and animation was dirtied in the meanwhile
+    if (forceAnimationUpdate_)
+    {
+        UpdateAnimation(frame);
+        forceAnimationUpdate_ = false;
+    }
+
     if (morphsDirty_)
     if (morphsDirty_)
         UpdateMorphs();
         UpdateMorphs();
 
 
@@ -277,7 +293,7 @@ void AnimatedModel::UpdateGeometry(const FrameInfo& frame)
 
 
 UpdateGeometryType AnimatedModel::GetUpdateGeometryType()
 UpdateGeometryType AnimatedModel::GetUpdateGeometryType()
 {
 {
-    if (morphsDirty_)
+    if (morphsDirty_ || forceAnimationUpdate_)
         return UPDATE_MAIN_THREAD;
         return UPDATE_MAIN_THREAD;
     else if (skinningDirty_)
     else if (skinningDirty_)
         return UPDATE_WORKER_THREAD;
         return UPDATE_WORKER_THREAD;
@@ -1150,7 +1166,7 @@ void AnimatedModel::UpdateAnimation(const FrameInfo& frame)
     // If using animation LOD, accumulate time and see if it is time to update
     // If using animation LOD, accumulate time and see if it is time to update
     if (animationLodBias_ > 0.0f && animationLodDistance_ > 0.0f)
     if (animationLodBias_ > 0.0f && animationLodDistance_ > 0.0f)
     {
     {
-        // Check for first time update
+        // Perform the first update always regardless of LOD timer
         if (animationLodTimer_ >= 0.0f)
         if (animationLodTimer_ >= 0.0f)
         {
         {
             animationLodTimer_ += animationLodBias_ * frame.timeStep_ * ANIMATION_LOD_BASESCALE;
             animationLodTimer_ += animationLodBias_ * frame.timeStep_ * ANIMATION_LOD_BASESCALE;

+ 2 - 0
Source/Atomic/Atomic3D/AnimatedModel.h

@@ -255,6 +255,8 @@ private:
     bool assignBonesPending_;
     bool assignBonesPending_;
     /// Whether bone creation is enabled, globally
     /// Whether bone creation is enabled, globally
     static bool boneCreationEnabled_;
     static bool boneCreationEnabled_;
+    /// Force animation update after becoming visible flag.
+    bool forceAnimationUpdate_;
 };
 };
 
 
 }
 }

+ 29 - 3
Source/Atomic/Atomic3D/AnimationController.cpp

@@ -44,6 +44,7 @@ static const unsigned char CTRL_STARTBONE = 0x2;
 static const unsigned char CTRL_AUTOFADE = 0x4;
 static const unsigned char CTRL_AUTOFADE = 0x4;
 static const unsigned char CTRL_SETTIME = 0x08;
 static const unsigned char CTRL_SETTIME = 0x08;
 static const unsigned char CTRL_SETWEIGHT = 0x10;
 static const unsigned char CTRL_SETWEIGHT = 0x10;
+static const unsigned char CTRL_REMOVEONCOMPLETION = 0x20;
 static const float EXTRA_ANIM_FADEOUT_TIME = 0.1f;
 static const float EXTRA_ANIM_FADEOUT_TIME = 0.1f;
 static const float COMMAND_STAY_TIME = 0.25f;
 static const float COMMAND_STAY_TIME = 0.25f;
 static const unsigned MAX_NODE_ANIMATION_STATES = 256;
 static const unsigned MAX_NODE_ANIMATION_STATES = 256;
@@ -70,7 +71,6 @@ void AnimationController::RegisterObject(Context* context)
         Variant::emptyBuffer, AM_NET | AM_LATESTDATA | AM_NOEDIT);
         Variant::emptyBuffer, AM_NET | AM_LATESTDATA | AM_NOEDIT);
     MIXED_ACCESSOR_ATTRIBUTE("Node Animation States", GetNodeAnimationStatesAttr, SetNodeAnimationStatesAttr, VariantVector,
     MIXED_ACCESSOR_ATTRIBUTE("Node Animation States", GetNodeAnimationStatesAttr, SetNodeAnimationStatesAttr, VariantVector,
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
         Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
-
 }
 }
 
 
 void AnimationController::OnSetEnabled()
 void AnimationController::OnSetEnabled()
@@ -128,7 +128,7 @@ void AnimationController::Update(float timeStep)
             }
             }
 
 
             // Remove if weight zero and target weight zero
             // Remove if weight zero and target weight zero
-            if (state->GetWeight() == 0.0f && (targetWeight == 0.0f || fadeTime == 0.0f))
+            if (state->GetWeight() == 0.0f && (targetWeight == 0.0f || fadeTime == 0.0f) && i->removeOnCompletion_)
                 remove = true;
                 remove = true;
         }
         }
 
 
@@ -176,7 +176,7 @@ bool AnimationController::Play(const String& name, unsigned char layer, bool loo
         }
         }
 
 
         if (!newAnimation)
         if (!newAnimation)
-            GetSubsystem<ResourceCache>()->GetResource<Animation>(name);
+            newAnimation = GetSubsystem<ResourceCache>()->GetResource<Animation>(name);
 
 
         state = AddAnimationState(newAnimation);
         state = AddAnimationState(newAnimation);
         if (!state)
         if (!state)
@@ -377,6 +377,19 @@ bool AnimationController::SetWeight(const String& name, float weight)
     return true;
     return true;
 }
 }
 
 
+bool AnimationController::SetRemoveOnCompletion(const String& name, bool removeOnCompletion)
+{
+    unsigned index;
+    AnimationState* state;
+    FindAnimation(name, index, state);
+    if (index == M_MAX_UNSIGNED || !state)
+        return false;
+
+    animations_[index].removeOnCompletion_ = removeOnCompletion;
+    MarkNetworkUpdate();
+    return true;
+}
+
 bool AnimationController::SetLooped(const String& name, bool enable)
 bool AnimationController::SetLooped(const String& name, bool enable)
 {
 {
     AnimationState* state = GetAnimationState(name);
     AnimationState* state = GetAnimationState(name);
@@ -517,6 +530,14 @@ float AnimationController::GetAutoFade(const String& name) const
     return index != M_MAX_UNSIGNED ? animations_[index].autoFadeTime_ : 0.0f;
     return index != M_MAX_UNSIGNED ? animations_[index].autoFadeTime_ : 0.0f;
 }
 }
 
 
+bool AnimationController::GetRemoveOnCompletion(const String& name) const
+{
+    unsigned index;
+    AnimationState* state;
+    FindAnimation(name, index, state);
+    return index != M_MAX_UNSIGNED ? animations_[index].removeOnCompletion_ : false;
+}
+
 AnimationState* AnimationController::GetAnimationState(const String& name) const
 AnimationState* AnimationController::GetAnimationState(const String& name) const
 {
 {
     return GetAnimationState(StringHash(name));
     return GetAnimationState(StringHash(name));
@@ -619,6 +640,9 @@ void AnimationController::SetNetAnimationsAttr(const PODVector<unsigned char>& v
             animations_[index].autoFadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade
             animations_[index].autoFadeTime_ = (float)buf.ReadUByte() / 64.0f; // 6 bits of decimal precision, max. 4 seconds fade
         else
         else
             animations_[index].autoFadeTime_ = 0.0f;
             animations_[index].autoFadeTime_ = 0.0f;
+        
+        animations_[index].removeOnCompletion_ = (ctrl & CTRL_REMOVEONCOMPLETION) != 0;
+        
         if (ctrl & CTRL_SETTIME)
         if (ctrl & CTRL_SETTIME)
         {
         {
             unsigned char setTimeRev = buf.ReadUByte();
             unsigned char setTimeRev = buf.ReadUByte();
@@ -731,6 +755,8 @@ const PODVector<unsigned char>& AnimationController::GetNetAnimationsAttr() cons
             ctrl |= CTRL_STARTBONE;
             ctrl |= CTRL_STARTBONE;
         if (i->autoFadeTime_ > 0.0f)
         if (i->autoFadeTime_ > 0.0f)
             ctrl |= CTRL_AUTOFADE;
             ctrl |= CTRL_AUTOFADE;
+        if (i->removeOnCompletion_)
+            ctrl |= CTRL_REMOVEONCOMPLETION;
         if (i->setTimeTtl_ > 0.0f)
         if (i->setTimeTtl_ > 0.0f)
             ctrl |= CTRL_SETTIME;
             ctrl |= CTRL_SETTIME;
         if (i->setWeightTtl_ > 0.0f)
         if (i->setWeightTtl_ > 0.0f)

+ 8 - 1
Source/Atomic/Atomic3D/AnimationController.h

@@ -47,7 +47,8 @@ struct AnimationControl
         setTime_(0),
         setTime_(0),
         setWeight_(0),
         setWeight_(0),
         setTimeRev_(0),
         setTimeRev_(0),
-        setWeightRev_(0)
+        setWeightRev_(0),
+        removeOnCompletion_(true)
     {
     {
     }
     }
 
 
@@ -75,6 +76,8 @@ struct AnimationControl
     unsigned char setTimeRev_;
     unsigned char setTimeRev_;
     /// Set weight command revision.
     /// Set weight command revision.
     unsigned char setWeightRev_;
     unsigned char setWeightRev_;
+    /// Sets whether this should automatically be removed when it finishes playing.
+    bool removeOnCompletion_;
 };
 };
 
 
 /// %Component that drives an AnimatedModel's animations.
 /// %Component that drives an AnimatedModel's animations.
@@ -124,6 +127,8 @@ public:
     bool SetSpeed(const String& name, float speed);
     bool SetSpeed(const String& name, float speed);
     /// Set animation autofade at end (non-looped animations only.) Zero time disables. Return true on success.
     /// Set animation autofade at end (non-looped animations only.) Zero time disables. Return true on success.
     bool SetAutoFade(const String& name, float fadeOutTime);
     bool SetAutoFade(const String& name, float fadeOutTime);
+    /// Set whether an animation auto-removes on completion.
+    bool SetRemoveOnCompletion(const String& name, bool removeOnCompletion);
 
 
     /// Return whether an animation is active. Note that non-looping animations that are being clamped at the end also return true.
     /// Return whether an animation is active. Note that non-looping animations that are being clamped at the end also return true.
     bool IsPlaying(const String& name) const;
     bool IsPlaying(const String& name) const;
@@ -155,6 +160,8 @@ public:
     float GetFadeTime(const String& name) const;
     float GetFadeTime(const String& name) const;
     /// Return animation autofade time.
     /// Return animation autofade time.
     float GetAutoFade(const String& name) const;
     float GetAutoFade(const String& name) const;
+    /// Return whether animation auto-removes on completion, or false if no such animation.
+    bool GetRemoveOnCompletion(const String& name) const;
     /// Find an animation state by animation name.
     /// Find an animation state by animation name.
     AnimationState* GetAnimationState(const String& name) const;
     AnimationState* GetAnimationState(const String& name) const;
     /// Find an animation state by animation name hash
     /// Find an animation state by animation name hash

+ 4 - 0
Source/Atomic/Atomic3D/DecalSet.cpp

@@ -42,6 +42,10 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
+#ifdef _MSC_VER
+#pragma warning(disable:6293)
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 84 - 8
Source/Atomic/Atomic3D/Terrain.cpp

@@ -89,6 +89,8 @@ Terrain::Terrain(Context* context) :
     patchSize_(DEFAULT_PATCH_SIZE),
     patchSize_(DEFAULT_PATCH_SIZE),
     lastPatchSize_(0),
     lastPatchSize_(0),
     numLodLevels_(1),
     numLodLevels_(1),
+    maxLodLevels_(MAX_LOD_LEVELS),
+    occlusionLodLevel_(M_MAX_UNSIGNED),
     smoothing_(false),
     smoothing_(false),
     visible_(true),
     visible_(true),
     castShadows_(false),
     castShadows_(false),
@@ -122,6 +124,7 @@ void Terrain::RegisterObject(Context* context)
         AM_DEFAULT);
         AM_DEFAULT);
     ATTRIBUTE("Vertex Spacing", Vector3, spacing_, DEFAULT_SPACING, AM_DEFAULT);
     ATTRIBUTE("Vertex Spacing", Vector3, spacing_, DEFAULT_SPACING, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Patch Size", GetPatchSize, SetPatchSizeAttr, int, DEFAULT_PATCH_SIZE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Patch Size", GetPatchSize, SetPatchSizeAttr, int, DEFAULT_PATCH_SIZE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Max LOD Levels", GetMaxLodLevels, SetMaxLodLevelsAttr, unsigned, MAX_LOD_LEVELS, AM_DEFAULT);
     ATTRIBUTE("Smooth Height Map", bool, smoothing_, false, AM_DEFAULT);
     ATTRIBUTE("Smooth Height Map", bool, smoothing_, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Is Occluder", IsOccluder, SetOccluder, bool, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Is Occluder", IsOccluder, SetOccluder, bool, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
@@ -134,6 +137,7 @@ void Terrain::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE("Light Mask", GetLightMask, SetLightMask, unsigned, DEFAULT_LIGHTMASK, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Light Mask", GetLightMask, SetLightMask, unsigned, DEFAULT_LIGHTMASK, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Shadow Mask", GetShadowMask, SetShadowMask, unsigned, DEFAULT_SHADOWMASK, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Shadow Mask", GetShadowMask, SetShadowMask, unsigned, DEFAULT_SHADOWMASK, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Zone Mask", GetZoneMask, SetZoneMask, unsigned, DEFAULT_ZONEMASK, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Zone Mask", GetZoneMask, SetZoneMask, unsigned, DEFAULT_ZONEMASK, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Occlusion LOD level", GetOcclusionLodLevel, SetOcclusionLodLevelAttr, unsigned, M_MAX_UNSIGNED, AM_DEFAULT);
 }
 }
 
 
 void Terrain::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 void Terrain::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
@@ -187,6 +191,31 @@ void Terrain::SetSpacing(const Vector3& spacing)
     }
     }
 }
 }
 
 
+void Terrain::SetMaxLodLevels(unsigned levels)
+{
+    levels = Clamp((int)levels, 1, MAX_LOD_LEVELS);
+    if (levels != maxLodLevels_)
+    {
+        maxLodLevels_ = levels;
+        lastPatchSize_ = 0; // Force full recreate
+
+        CreateGeometry();
+        MarkNetworkUpdate();
+    }
+}
+
+void Terrain::SetOcclusionLodLevel(unsigned level)
+{
+    if (level != occlusionLodLevel_)
+    {
+        occlusionLodLevel_ = level;
+        lastPatchSize_ = 0; // Force full recreate
+
+        CreateGeometry();
+        MarkNetworkUpdate();
+    }
+}
+
 void Terrain::SetSmoothing(bool enable)
 void Terrain::SetSmoothing(bool enable)
 {
 {
     if (enable != smoothing_)
     if (enable != smoothing_)
@@ -468,20 +497,28 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
     VertexBuffer* vertexBuffer = patch->GetVertexBuffer();
     VertexBuffer* vertexBuffer = patch->GetVertexBuffer();
     Geometry* geometry = patch->GetGeometry();
     Geometry* geometry = patch->GetGeometry();
     Geometry* maxLodGeometry = patch->GetMaxLodGeometry();
     Geometry* maxLodGeometry = patch->GetMaxLodGeometry();
-    Geometry* minLodGeometry = patch->GetMinLodGeometry();
+    Geometry* occlusionGeometry = patch->GetOcclusionGeometry();
 
 
     if (vertexBuffer->GetVertexCount() != row * row)
     if (vertexBuffer->GetVertexCount() != row * row)
         vertexBuffer->SetSize(row * row, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
         vertexBuffer->SetSize(row * row, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
 
 
     SharedArrayPtr<unsigned char> cpuVertexData(new unsigned char[row * row * sizeof(Vector3)]);
     SharedArrayPtr<unsigned char> cpuVertexData(new unsigned char[row * row * sizeof(Vector3)]);
+    SharedArrayPtr<unsigned char> occlusionCpuVertexData(new unsigned char[row * row * sizeof(Vector3)]);
 
 
     float* vertexData = (float*)vertexBuffer->Lock(0, vertexBuffer->GetVertexCount());
     float* vertexData = (float*)vertexBuffer->Lock(0, vertexBuffer->GetVertexCount());
     float* positionData = (float*)cpuVertexData.Get();
     float* positionData = (float*)cpuVertexData.Get();
+    float* occlusionData = (float*)occlusionCpuVertexData.Get();
     BoundingBox box;
     BoundingBox box;
 
 
+    unsigned occlusionLevel = occlusionLodLevel_;
+    if (occlusionLevel > numLodLevels_ - 1)
+        occlusionLevel = numLodLevels_ - 1;
+
     if (vertexData)
     if (vertexData)
     {
     {
         const IntVector2& coords = patch->GetCoordinates();
         const IntVector2& coords = patch->GetCoordinates();
+        int lodExpand = (1 << (occlusionLevel)) - 1;
+        int halfLodExpand = (1 << (occlusionLevel)) / 2;
 
 
         for (int z = 0; z <= patchSize_; ++z)
         for (int z = 0; z <= patchSize_; ++z)
         {
         {
@@ -501,6 +538,25 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
 
 
                 box.Merge(position);
                 box.Merge(position);
 
 
+                // For vertices that are part of the occlusion LOD, calculate the minimum height in the neighborhood
+                // to prevent false positive occlusion due to inaccuracy between occlusion LOD & visible LOD
+                float minHeight = position.y_;
+                if (halfLodExpand > 0 && (x & lodExpand) == 0 && (z & lodExpand) == 0)
+                {
+                    int minX = Max(xPos - halfLodExpand, 0);
+                    int maxX = Min(xPos + halfLodExpand, numVertices_.x_ - 1);
+                    int minZ = Max(zPos - halfLodExpand, 0);
+                    int maxZ = Min(zPos + halfLodExpand, numVertices_.y_ - 1);
+                    for (int nZ = minZ; nZ <= maxZ; ++nZ)
+                    {
+                        for (int nX = minX; nX <= maxX; ++nX)
+                            minHeight = Min(minHeight, GetRawHeight(nX, nZ));
+                    }
+                }
+                *occlusionData++ = position.x_;
+                *occlusionData++ = minHeight;
+                *occlusionData++ = position.z_;
+
                 // Normal
                 // Normal
                 Vector3 normal = GetRawNormal(xPos, zPos);
                 Vector3 normal = GetRawNormal(xPos, zPos);
                 *vertexData++ = normal.x_;
                 *vertexData++ = normal.x_;
@@ -529,7 +585,7 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
 
 
     if (drawRanges_.Size())
     if (drawRanges_.Size())
     {
     {
-        unsigned lastDrawRange = drawRanges_.Size() - 1;
+        unsigned occlusionDrawRange = occlusionLevel << 4;
 
 
         geometry->SetIndexBuffer(indexBuffer_);
         geometry->SetIndexBuffer(indexBuffer_);
         geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false);
         geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false);
@@ -537,13 +593,11 @@ void Terrain::CreatePatchGeometry(TerrainPatch* patch)
         maxLodGeometry->SetIndexBuffer(indexBuffer_);
         maxLodGeometry->SetIndexBuffer(indexBuffer_);
         maxLodGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false);
         maxLodGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false);
         maxLodGeometry->SetRawVertexData(cpuVertexData, sizeof(Vector3), MASK_POSITION);
         maxLodGeometry->SetRawVertexData(cpuVertexData, sizeof(Vector3), MASK_POSITION);
-        minLodGeometry->SetIndexBuffer(indexBuffer_);
-        minLodGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[lastDrawRange].first_, drawRanges_[lastDrawRange].second_, false);
-        minLodGeometry->SetRawVertexData(cpuVertexData, sizeof(Vector3), MASK_POSITION);
+        occlusionGeometry->SetIndexBuffer(indexBuffer_);
+        occlusionGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[occlusionDrawRange].first_, drawRanges_[occlusionDrawRange].second_, false);
+        occlusionGeometry->SetRawVertexData(occlusionCpuVertexData, sizeof(Vector3), MASK_POSITION);
     }
     }
 
 
-    // Offset the occlusion geometry by vertex spacing to reduce possibility of over-aggressive occlusion
-    patch->SetOcclusionOffset(-0.5f * (spacing_.x_ + spacing_.z_));
     patch->ResetLod();
     patch->ResetLod();
 }
 }
 
 
@@ -600,6 +654,28 @@ void Terrain::SetPatchSizeAttr(int value)
     }
     }
 }
 }
 
 
+void Terrain::SetMaxLodLevelsAttr(unsigned value)
+{
+    value = Clamp((int)value, 1, MAX_LOD_LEVELS);
+
+    if (value != maxLodLevels_)
+    {
+        maxLodLevels_ = value;
+        lastPatchSize_ = 0; // Force full recreate
+        recreateTerrain_ = true;
+    }
+}
+
+void Terrain::SetOcclusionLodLevelAttr(unsigned value)
+{
+    if (value != occlusionLodLevel_)
+    {
+        occlusionLodLevel_ = value;
+        lastPatchSize_ = 0; // Force full recreate
+        recreateTerrain_ = true;
+    }
+}
+
 ResourceRef Terrain::GetMaterialAttr() const
 ResourceRef Terrain::GetMaterialAttr() const
 {
 {
     return GetResourceRef(material_, Material::GetTypeStatic());
     return GetResourceRef(material_, Material::GetTypeStatic());
@@ -624,7 +700,7 @@ void Terrain::CreateGeometry()
     // Determine number of LOD levels
     // Determine number of LOD levels
     unsigned lodSize = (unsigned)patchSize_;
     unsigned lodSize = (unsigned)patchSize_;
     numLodLevels_ = 1;
     numLodLevels_ = 1;
-    while (lodSize > MIN_PATCH_SIZE && numLodLevels_ < MAX_LOD_LEVELS)
+    while (lodSize > MIN_PATCH_SIZE && numLodLevels_ < maxLodLevels_)
     {
     {
         lodSize >>= 1;
         lodSize >>= 1;
         ++numLodLevels_;
         ++numLodLevels_;

+ 19 - 1
Source/Atomic/Atomic3D/Terrain.h

@@ -57,6 +57,10 @@ public:
     void SetPatchSize(int size);
     void SetPatchSize(int size);
     /// Set vertex (XZ) and height (Y) spacing.
     /// Set vertex (XZ) and height (Y) spacing.
     void SetSpacing(const Vector3& spacing);
     void SetSpacing(const Vector3& spacing);
+    /// Set maximum number of LOD levels for terrain patches. This can be between 1-4.
+    void SetMaxLodLevels(unsigned levels);
+    /// Set LOD level used for terrain patch occlusion. By default (M_MAX_UNSIGNED) the coarsest. Since the LOD level used needs to be fixed, using finer LOD levels may result in false positive occlusion in cases where the actual rendered geometry is coarser, so use with caution.
+    void SetOcclusionLodLevel(unsigned level);
     /// Set smoothing of heightmap.
     /// Set smoothing of heightmap.
     void SetSmoothing(bool enable);
     void SetSmoothing(bool enable);
     /// Set heightmap image. Dimensions should be a power of two + 1. Uses 8-bit grayscale, or optionally red as MSB and green as LSB for 16-bit accuracy. Return true if successful.
     /// Set heightmap image. Dimensions should be a power of two + 1. Uses 8-bit grayscale, or optionally red as MSB and green as LSB for 16-bit accuracy. Return true if successful.
@@ -81,7 +85,7 @@ public:
     void SetMaxLights(unsigned num);
     void SetMaxLights(unsigned num);
     /// Set shadowcaster flag for patches.
     /// Set shadowcaster flag for patches.
     void SetCastShadows(bool enable);
     void SetCastShadows(bool enable);
-    /// Set occlusion flag for patches. Occlusion uses the coarsest LOD and may potentially be too aggressive, so use with caution.
+    /// Set occlusion flag for patches. Occlusion uses the coarsest LOD by default.
     void SetOccluder(bool enable);
     void SetOccluder(bool enable);
     /// Set occludee flag for patches.
     /// Set occludee flag for patches.
     void SetOccludee(bool enable);
     void SetOccludee(bool enable);
@@ -100,6 +104,12 @@ public:
     /// Return heightmap size in patches.
     /// Return heightmap size in patches.
     const IntVector2& GetNumPatches() const { return numPatches_; }
     const IntVector2& GetNumPatches() const { return numPatches_; }
 
 
+    /// Return maximum number of LOD levels for terrain patches. This can be between 1-4.
+    unsigned GetMaxLodLevels() const { return maxLodLevels_; }
+
+    /// Return LOD level used for occlusion.
+    unsigned GetOcclusionLodLevel() const { return occlusionLodLevel_; }
+
     /// Return whether smoothing is in use.
     /// Return whether smoothing is in use.
     bool GetSmoothing() const { return smoothing_; }
     bool GetSmoothing() const { return smoothing_; }
 
 
@@ -167,6 +177,10 @@ public:
     void SetMaterialAttr(const ResourceRef& value);
     void SetMaterialAttr(const ResourceRef& value);
     /// Set patch size attribute.
     /// Set patch size attribute.
     void SetPatchSizeAttr(int value);
     void SetPatchSizeAttr(int value);
+    /// Set max LOD levels attribute.
+    void SetMaxLodLevelsAttr(unsigned value);
+    /// Set occlusion LOD level attribute.
+    void SetOcclusionLodLevelAttr(unsigned value);
     /// Return heightmap attribute.
     /// Return heightmap attribute.
     ResourceRef GetHeightMapAttr() const;
     ResourceRef GetHeightMapAttr() const;
     /// Return material attribute.
     /// Return material attribute.
@@ -228,6 +242,10 @@ private:
     int lastPatchSize_;
     int lastPatchSize_;
     /// Number of terrain LOD levels.
     /// Number of terrain LOD levels.
     unsigned numLodLevels_;
     unsigned numLodLevels_;
+    /// Maximum number of LOD levels.
+    unsigned maxLodLevels_;
+    /// LOD level used for occlusion.
+    unsigned occlusionLodLevel_;
     /// Smoothing enable flag.
     /// Smoothing enable flag.
     bool smoothing_;
     bool smoothing_;
     /// Visible flag.
     /// Visible flag.

+ 11 - 22
Source/Atomic/Atomic3D/TerrainPatch.cpp

@@ -24,6 +24,7 @@
 
 
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Graphics/Camera.h"
 #include "../Graphics/Camera.h"
+#include "../Graphics/DebugRenderer.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/Geometry.h"
 #include "../Graphics/IndexBuffer.h"
 #include "../Graphics/IndexBuffer.h"
 #include "../Graphics/Material.h"
 #include "../Graphics/Material.h"
@@ -48,15 +49,14 @@ TerrainPatch::TerrainPatch(Context* context) :
     Drawable(context, DRAWABLE_GEOMETRY),
     Drawable(context, DRAWABLE_GEOMETRY),
     geometry_(new Geometry(context)),
     geometry_(new Geometry(context)),
     maxLodGeometry_(new Geometry(context)),
     maxLodGeometry_(new Geometry(context)),
-    minLodGeometry_(new Geometry(context)),
+    occlusionGeometry_(new Geometry(context)),
     vertexBuffer_(new VertexBuffer(context)),
     vertexBuffer_(new VertexBuffer(context)),
     coordinates_(IntVector2::ZERO),
     coordinates_(IntVector2::ZERO),
-    lodLevel_(0),
-    occlusionOffset_(0.0f)
+    lodLevel_(0)
 {
 {
     geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     maxLodGeometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     maxLodGeometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
-    minLodGeometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
+    occlusionGeometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
 
 
     batches_.Resize(1);
     batches_.Resize(1);
     batches_[0].geometry_ = geometry_;
     batches_[0].geometry_ = geometry_;
@@ -176,7 +176,7 @@ unsigned TerrainPatch::GetNumOccluderTriangles()
     if (mat && !mat->GetOcclusion())
     if (mat && !mat->GetOcclusion())
         return 0;
         return 0;
     else
     else
-        return minLodGeometry_->GetIndexCount() / 3;
+        return occlusionGeometry_->GetIndexCount() / 3;
 }
 }
 
 
 bool TerrainPatch::DrawOcclusion(OcclusionBuffer* buffer)
 bool TerrainPatch::DrawOcclusion(OcclusionBuffer* buffer)
@@ -198,27 +198,21 @@ bool TerrainPatch::DrawOcclusion(OcclusionBuffer* buffer)
     unsigned indexSize;
     unsigned indexSize;
     unsigned elementMask;
     unsigned elementMask;
 
 
-    minLodGeometry_->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask);
+    occlusionGeometry_->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask);
     // Check for valid geometry data
     // Check for valid geometry data
     if (!vertexData || !indexData)
     if (!vertexData || !indexData)
         return true;
         return true;
 
 
-    const Matrix3x4& worldTransform = node_->GetWorldTransform();
-
-    Matrix3x4 occlusionTransform(worldTransform.Translation() + worldTransform * Vector4(0.0f, occlusionOffset_, 0.0f,
-        0.0f), worldTransform.Rotation(), worldTransform.Scale());
-
     // Draw and check for running out of triangles
     // Draw and check for running out of triangles
-    return buffer->Draw(occlusionTransform, vertexData, vertexSize, indexData, indexSize, minLodGeometry_->GetIndexStart(),
-        minLodGeometry_->GetIndexCount());
+    return buffer->Draw(node_->GetWorldTransform(), vertexData, vertexSize, indexData, indexSize, occlusionGeometry_->GetIndexStart(),
+        occlusionGeometry_->GetIndexCount());
 }
 }
 
 
 void TerrainPatch::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 void TerrainPatch::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 {
 {
-    // Intentionally no action
+    // Intentionally no operation
 }
 }
 
 
-
 void TerrainPatch::SetOwner(Terrain* terrain)
 void TerrainPatch::SetOwner(Terrain* terrain)
 {
 {
     owner_ = terrain;
     owner_ = terrain;
@@ -248,11 +242,6 @@ void TerrainPatch::SetCoordinates(const IntVector2& coordinates)
     coordinates_ = coordinates;
     coordinates_ = coordinates;
 }
 }
 
 
-void TerrainPatch::SetOcclusionOffset(float offset)
-{
-    occlusionOffset_ = offset;
-}
-
 void TerrainPatch::ResetLod()
 void TerrainPatch::ResetLod()
 {
 {
     lodLevel_ = 0;
     lodLevel_ = 0;
@@ -268,9 +257,9 @@ Geometry* TerrainPatch::GetMaxLodGeometry() const
     return maxLodGeometry_;
     return maxLodGeometry_;
 }
 }
 
 
-Geometry* TerrainPatch::GetMinLodGeometry() const
+Geometry* TerrainPatch::GetOcclusionGeometry() const
 {
 {
-    return minLodGeometry_;
+    return occlusionGeometry_;
 }
 }
 
 
 VertexBuffer* TerrainPatch::GetVertexBuffer() const
 VertexBuffer* TerrainPatch::GetVertexBuffer() const

+ 5 - 12
Source/Atomic/Atomic3D/TerrainPatch.h

@@ -71,17 +71,15 @@ public:
     void SetBoundingBox(const BoundingBox& box);
     void SetBoundingBox(const BoundingBox& box);
     /// Set patch coordinates.
     /// Set patch coordinates.
     void SetCoordinates(const IntVector2& coordinates);
     void SetCoordinates(const IntVector2& coordinates);
-    /// Set vertical offset for occlusion geometry. Should be negative.
-    void SetOcclusionOffset(float offset);
     /// Reset to LOD level 0.
     /// Reset to LOD level 0.
     void ResetLod();
     void ResetLod();
 
 
     /// Return visible geometry.
     /// Return visible geometry.
     Geometry* GetGeometry() const;
     Geometry* GetGeometry() const;
-    /// Return max LOD geometry.
+    /// Return max LOD geometry. Used for decals.
     Geometry* GetMaxLodGeometry() const;
     Geometry* GetMaxLodGeometry() const;
-    /// Return min LOD geometry.
-    Geometry* GetMinLodGeometry() const;
+    /// Return geometry used for occlusion.
+    Geometry* GetOcclusionGeometry() const;
     /// Return vertex buffer.
     /// Return vertex buffer.
     VertexBuffer* GetVertexBuffer() const;
     VertexBuffer* GetVertexBuffer() const;
     /// Return owner terrain.
     /// Return owner terrain.
@@ -108,9 +106,6 @@ public:
     /// Return current LOD level.
     /// Return current LOD level.
     unsigned GetLodLevel() const { return lodLevel_; }
     unsigned GetLodLevel() const { return lodLevel_; }
 
 
-    /// Return vertical offset for occlusion geometry..
-    float GetOcclusionOffset() const { return occlusionOffset_; }
-
 protected:
 protected:
     /// Recalculate the world-space bounding box.
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
     virtual void OnWorldBoundingBoxUpdate();
@@ -123,8 +118,8 @@ private:
     SharedPtr<Geometry> geometry_;
     SharedPtr<Geometry> geometry_;
     /// Geometry that is locked to the max LOD level. Used for decals.
     /// Geometry that is locked to the max LOD level. Used for decals.
     SharedPtr<Geometry> maxLodGeometry_;
     SharedPtr<Geometry> maxLodGeometry_;
-    /// Geometry that is locked to the minimum LOD level. Used for occlusion.
-    SharedPtr<Geometry> minLodGeometry_;
+    /// Geometry that is used for occlusion.
+    SharedPtr<Geometry> occlusionGeometry_;
     /// Vertex buffer.
     /// Vertex buffer.
     SharedPtr<VertexBuffer> vertexBuffer_;
     SharedPtr<VertexBuffer> vertexBuffer_;
     /// Parent terrain.
     /// Parent terrain.
@@ -143,8 +138,6 @@ private:
     IntVector2 coordinates_;
     IntVector2 coordinates_;
     /// Current LOD level.
     /// Current LOD level.
     unsigned lodLevel_;
     unsigned lodLevel_;
-    /// Vertical offset for occlusion geometry.
-    float occlusionOffset_;
 };
 };
 
 
 }
 }

+ 4 - 0
Source/Atomic/Audio/Audio.cpp

@@ -36,6 +36,10 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
+#ifdef _MSC_VER
+  #pragma warning(disable:6293)
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 12 - 8
Source/Atomic/Container/Str.cpp

@@ -28,6 +28,10 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
+#ifdef _MSC_VER
+#pragma warning(disable:6293)
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 
@@ -559,7 +563,7 @@ Vector<String> String::Split(char separator) const
     return Split(CString(), separator);
     return Split(CString(), separator);
 }
 }
 
 
-void String::Join(const Vector<String>& subStrings, String glue)
+void String::Join(const Vector<String>& subStrings, const String& glue)
 {
 {
     *this = Joined(subStrings, glue);
     *this = Joined(subStrings, glue);
 }
 }
@@ -1009,9 +1013,9 @@ unsigned String::DecodeUTF16(const wchar_t*& src)
 {
 {
     if (src == 0)
     if (src == 0)
         return 0;
         return 0;
-    
+
     unsigned short word1 = *src;
     unsigned short word1 = *src;
-    
+
     // Check if we are at a low surrogate
     // Check if we are at a low surrogate
     word1 = *src++;
     word1 = *src++;
     if (word1 >= 0xdc00 && word1 < 0xe000)
     if (word1 >= 0xdc00 && word1 < 0xe000)
@@ -1020,7 +1024,7 @@ unsigned String::DecodeUTF16(const wchar_t*& src)
             ++src;
             ++src;
         return '?';
         return '?';
     }
     }
-    
+
     if (word1 < 0xd800 || word1 >= 0xe00)
     if (word1 < 0xd800 || word1 >= 0xe00)
         return word1;
         return word1;
     else
     else
@@ -1085,7 +1089,7 @@ Vector<String> String::Split(const char* str, char separator)
     return ret;
     return ret;
 }
 }
 
 
-String String::Joined(const Vector<String>& subStrings, String glue)
+String String::Joined(const Vector<String>& subStrings, const String& glue)
 {
 {
     if (subStrings.Empty())
     if (subStrings.Empty())
         return String();
         return String();
@@ -1261,7 +1265,7 @@ WString::WString(const String& str) :
 #ifdef WIN32
 #ifdef WIN32
     unsigned neededSize = 0;
     unsigned neededSize = 0;
     wchar_t temp[3];
     wchar_t temp[3];
-    
+
     unsigned byteOffset = 0;
     unsigned byteOffset = 0;
     while (byteOffset < str.Length())
     while (byteOffset < str.Length())
     {
     {
@@ -1269,9 +1273,9 @@ WString::WString(const String& str) :
         String::EncodeUTF16(dest, str.NextUTF8Char(byteOffset));
         String::EncodeUTF16(dest, str.NextUTF8Char(byteOffset));
         neededSize += dest - temp;
         neededSize += dest - temp;
     }
     }
-    
+
     Resize(neededSize);
     Resize(neededSize);
-    
+
     byteOffset = 0;
     byteOffset = 0;
     wchar_t* dest = buffer_;
     wchar_t* dest = buffer_;
     while (byteOffset < str.Length())
     while (byteOffset < str.Length())

+ 3 - 3
Source/Atomic/Container/Str.h

@@ -390,7 +390,7 @@ public:
     /// Return substrings split by a separator char.
     /// Return substrings split by a separator char.
     Vector<String> Split(char separator) const;
     Vector<String> Split(char separator) const;
     /// Join substrings with a 'glue' string.
     /// Join substrings with a 'glue' string.
-    void Join(const Vector<String>& subStrings, String glue);
+    void Join(const Vector<String>& subStrings, const String& glue);
     /// Return index to the first occurrence of a string, or NPOS if not found.
     /// Return index to the first occurrence of a string, or NPOS if not found.
     unsigned Find(const String& str, unsigned startPos = 0, bool caseSensitive = true) const;
     unsigned Find(const String& str, unsigned startPos = 0, bool caseSensitive = true) const;
     /// Return index to the first occurrence of a character, or NPOS if not found.
     /// Return index to the first occurrence of a character, or NPOS if not found.
@@ -421,7 +421,7 @@ public:
     /// Return comparison result with a C string.
     /// Return comparison result with a C string.
     int Compare(const char* str, bool caseSensitive = true) const;
     int Compare(const char* str, bool caseSensitive = true) const;
 
 
-    /// Return whether contains a specific occurence of a string.
+    /// Return whether contains a specific occurrence of a string.
     bool Contains(const String& str, bool caseSensitive = true) const { return Find(str, 0, caseSensitive) != NPOS; }
     bool Contains(const String& str, bool caseSensitive = true) const { return Find(str, 0, caseSensitive) != NPOS; }
 
 
     /// Return whether contains a specific character.
     /// Return whether contains a specific character.
@@ -465,7 +465,7 @@ public:
     /// Return substrings split by a separator char.
     /// Return substrings split by a separator char.
     static Vector<String> Split(const char* str, char separator);
     static Vector<String> Split(const char* str, char separator);
     /// Return a string by joining substrings with a 'glue' string.
     /// Return a string by joining substrings with a 'glue' string.
-    static String Joined(const Vector<String>& subStrings, String glue);
+    static String Joined(const Vector<String>& subStrings, const String& glue);
     /// Encode Unicode character to UTF8. Pointer will be incremented.
     /// Encode Unicode character to UTF8. Pointer will be incremented.
     static void EncodeUTF8(char*& dest, unsigned unicodeChar);
     static void EncodeUTF8(char*& dest, unsigned unicodeChar);
     /// Decode Unicode character from UTF8. Pointer will be incremented.
     /// Decode Unicode character from UTF8. Pointer will be incremented.

+ 19 - 0
Source/Atomic/Container/Vector.h

@@ -28,6 +28,11 @@
 #include <cstring>
 #include <cstring>
 #include <new>
 #include <new>
 
 
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:6293)
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 
@@ -170,7 +175,17 @@ public:
     }
     }
 
 
     /// Add an element at the end.
     /// Add an element at the end.
+#ifndef COVERITY_SCAN_MODEL
     void Push(const T& value) { Resize(size_ + 1, &value); }
     void Push(const T& value) { Resize(size_ + 1, &value); }
+#else
+    // FIXME: Attempt had been made to use this model in the Coverity-Scan model file without any success
+    // Probably because the model had generated a different mangled name than the one used by static analyzer
+    void Push(const T& value)
+    {
+        T array[] = {value};
+        Resize(size_ + 1, array);
+    }
+#endif
 
 
     /// Add another vector at the end.
     /// Add another vector at the end.
     void Push(const Vector<T>& vector) { Resize(size_ + vector.size_, vector.Buffer()); }
     void Push(const Vector<T>& vector) { Resize(size_ + vector.size_, vector.Buffer()); }
@@ -948,3 +963,7 @@ template <class T> typename Atomic::PODVector<T>::Iterator begin(Atomic::PODVect
 template <class T> typename Atomic::PODVector<T>::Iterator end(Atomic::PODVector<T>& v) { return v.End(); }
 template <class T> typename Atomic::PODVector<T>::Iterator end(Atomic::PODVector<T>& v) { return v.End(); }
 
 
 }
 }
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif

+ 1 - 1
Source/Atomic/Core/Attribute.h

@@ -34,7 +34,7 @@ static const unsigned AM_EDIT = 0x0;
 static const unsigned AM_FILE = 0x1;
 static const unsigned AM_FILE = 0x1;
 /// Attribute used for network replication.
 /// Attribute used for network replication.
 static const unsigned AM_NET = 0x2;
 static const unsigned AM_NET = 0x2;
-/// Attribute used for both file serialization and network replication (default.)
+/// Attribute used for both file serialization and network replication (default).
 static const unsigned AM_DEFAULT = 0x3;
 static const unsigned AM_DEFAULT = 0x3;
 /// Attribute should use latest data grouping instead of delta update in network replication.
 /// Attribute should use latest data grouping instead of delta update in network replication.
 static const unsigned AM_LATESTDATA = 0x4;
 static const unsigned AM_LATESTDATA = 0x4;

+ 3 - 5
Source/Atomic/Core/ProcessUtils.cpp

@@ -24,6 +24,8 @@
 
 
 #include "../Core/ProcessUtils.h"
 #include "../Core/ProcessUtils.h"
 
 
+#include <SDL/include/SDL.h>
+
 #include <cstdio>
 #include <cstdio>
 #include <fcntl.h>
 #include <fcntl.h>
 
 
@@ -123,11 +125,7 @@ void InitFPU()
 
 
 void ErrorDialog(const String& title, const String& message)
 void ErrorDialog(const String& title, const String& message)
 {
 {
-#ifdef WIN32
-    MessageBoxW(0, WString(message).CString(), WString(title).CString(), 0);
-#else
-    PrintLine(message, true);
-#endif
+    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title.CString(), message.CString(), 0);
 }
 }
 
 
 void ErrorExit(const String& message, int exitCode)
 void ErrorExit(const String& message, int exitCode)

+ 93 - 0
Source/Atomic/Core/Spline.cpp

@@ -31,6 +31,9 @@ namespace Atomic
 const char* interpolationModeNames[] =
 const char* interpolationModeNames[] =
 {
 {
     "Bezier",
     "Bezier",
+    "Catmull-Rom",
+    "Linear",
+    "Catmull-Rom Full",
     0
     0
 };
 };
 
 
@@ -70,6 +73,33 @@ Variant Spline::GetPoint(float f) const
     {
     {
     case BEZIER_CURVE:
     case BEZIER_CURVE:
         return BezierInterpolation(knots_, f);
         return BezierInterpolation(knots_, f);
+    case CATMULL_ROM_CURVE:
+        return CatmullRomInterpolation(knots_, f);
+    case LINEAR_CURVE:
+        return LinearInterpolation(knots_, f);
+    case CATMULL_ROM_FULL_CURVE:
+        {
+            /// \todo Do not allocate a new vector each time
+            Vector<Variant> fullKnots;
+            if (knots_.Size() > 1)
+            {
+                // Non-cyclic case: duplicate start and end
+                if (knots_.Front() != knots_.Back())
+                {
+                    fullKnots.Push(knots_.Front());
+                    fullKnots.Push(knots_);
+                    fullKnots.Push(knots_.Back());
+                }
+                // Cyclic case: smooth the tangents
+                else
+                {
+                    fullKnots.Push(knots_[knots_.Size() - 2]);
+                    fullKnots.Push(knots_);
+                    fullKnots.Push(knots_[1]);
+                }
+            }
+            return CatmullRomInterpolation(fullKnots, f);
+        }
 
 
     default:
     default:
         LOGERROR("Unsupported interpolation mode");
         LOGERROR("Unsupported interpolation mode");
@@ -135,6 +165,7 @@ Variant Spline::BezierInterpolation(const Vector<Variant>& knots, float t) const
     }
     }
     else
     else
     {
     {
+        /// \todo Do not allocate a new vector each time
         Vector<Variant> interpolatedKnots;
         Vector<Variant> interpolatedKnots;
         for (unsigned i = 1; i < knots.Size(); i++)
         for (unsigned i = 1; i < knots.Size(); i++)
         {
         {
@@ -156,6 +187,68 @@ Variant Spline::BezierInterpolation(const Vector<Variant>& knots, float t) const
     }
     }
 }
 }
 
 
+template <typename T> Variant CalculateCatmullRom(const T& p0, const T& p1, const T& p2, const T& p3, float t, float t2, float t3)
+{
+    return Variant(0.5f * ((2.0f * p1) + (-p0 + p2) * t +
+        (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 +
+        (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3));
+}
+
+Variant Spline::CatmullRomInterpolation(const Vector<Variant>& knots, float t) const
+{
+    if (knots.Size() < 4)
+        return Variant::EMPTY;
+    else
+    {
+        if (t >= 1.f)
+            return knots[knots.Size() - 2];
+
+        int originIndex = static_cast<int>(t * (knots.Size() - 3));
+        t = fmodf(t * (knots.Size() - 3), 1.f);
+        float t2 = t * t;
+        float t3 = t2 * t;
+
+        switch (knots[originIndex].GetType())
+        {
+        case VAR_FLOAT:
+            return CalculateCatmullRom(knots[originIndex].GetFloat(), knots[originIndex + 1].GetFloat(),
+                knots[originIndex + 2].GetFloat(), knots[originIndex + 3].GetFloat(), t, t2, t3);
+        case VAR_VECTOR2:
+            return CalculateCatmullRom(knots[originIndex].GetVector2(), knots[originIndex + 1].GetVector2(),
+                knots[originIndex + 2].GetVector2(), knots[originIndex + 3].GetVector2(), t, t2, t3);
+        case VAR_VECTOR3:
+            return CalculateCatmullRom(knots[originIndex].GetVector3(), knots[originIndex + 1].GetVector3(),
+                knots[originIndex + 2].GetVector3(), knots[originIndex + 3].GetVector3(), t, t2, t3);
+        case VAR_VECTOR4:
+            return CalculateCatmullRom(knots[originIndex].GetVector4(), knots[originIndex + 1].GetVector4(),
+                knots[originIndex + 2].GetVector4(), knots[originIndex + 3].GetVector4(), t, t2, t3);
+        case VAR_COLOR:
+            return CalculateCatmullRom(knots[originIndex].GetColor(), knots[originIndex + 1].GetColor(),
+                knots[originIndex + 2].GetColor(), knots[originIndex + 3].GetColor(), t, t2, t3);
+        case VAR_DOUBLE:
+            return CalculateCatmullRom(knots[originIndex].GetDouble(), knots[originIndex + 1].GetDouble(),
+                knots[originIndex + 2].GetDouble(), knots[originIndex + 3].GetDouble(), t, t2, t3);
+        default:
+            return Variant::EMPTY;
+        }
+    }
+}
+
+Variant Spline::LinearInterpolation(const Vector<Variant>& knots, float t) const
+{
+    if (knots.Size() < 2)
+        return Variant::EMPTY;
+    else
+    {
+        if (t >= 1.f)
+            return knots.Back();
+
+        int originIndex = Clamp((int)(t * (knots.Size() - 1)), 0, (int)(knots.Size() - 2));
+        t = fmodf(t * (knots.Size() - 1), 1.f);
+        return LinearInterpolation(knots[originIndex], knots[originIndex + 1], t);
+    }
+}
+
 Variant Spline::LinearInterpolation(const Variant& lhs, const Variant& rhs, float t) const
 Variant Spline::LinearInterpolation(const Variant& lhs, const Variant& rhs, float t) const
 {
 {
     switch (lhs.GetType())
     switch (lhs.GetType())

+ 31 - 20
Source/Atomic/Core/Spline.h

@@ -33,7 +33,14 @@ namespace Atomic
 
 
 enum InterpolationMode
 enum InterpolationMode
 {
 {
-    BEZIER_CURVE = 0
+    /// Bezier interpolation.
+    BEZIER_CURVE = 0,
+    /// Catmull-Rom interpolation. The first and last knots control velocity and are not included on the path.
+    CATMULL_ROM_CURVE,
+    /// Linear interpolation.
+    LINEAR_CURVE,
+    /// Catmull-Rom full path interpolation. Start and end knots are duplicated or looped as necessary to move through the full path.
+    CATMULL_ROM_FULL_CURVE
 };
 };
 
 
 /// Spline class to get a point on it based off the interpolation mode.
 /// Spline class to get a point on it based off the interpolation mode.
@@ -42,9 +49,9 @@ class ATOMIC_API Spline
 public:
 public:
     /// Default constructor.
     /// Default constructor.
     Spline();
     Spline();
-    /// Constructor setting InterpolationMode.
+    /// Constructor setting interpolation mode.
     Spline(InterpolationMode mode);
     Spline(InterpolationMode mode);
-    /// Constructor setting Knots and InterpolationMode.
+    /// Constructor setting knots and interpolation mode.
     Spline(const Vector<Variant>& knots, InterpolationMode mode = BEZIER_CURVE);
     Spline(const Vector<Variant>& knots, InterpolationMode mode = BEZIER_CURVE);
     /// Copy constructor.
     /// Copy constructor.
     Spline(const Spline& rhs);
     Spline(const Spline& rhs);
@@ -62,55 +69,59 @@ public:
         return (knots_ == rhs.knots_ && interpolationMode_ == rhs.interpolationMode_);
         return (knots_ == rhs.knots_ && interpolationMode_ == rhs.interpolationMode_);
     }
     }
 
 
-    /// Non Equality operator.
+    /// Inequality operator.
     bool operator !=(const Spline& rhs) const
     bool operator !=(const Spline& rhs) const
     {
     {
         return !(*this == rhs);
         return !(*this == rhs);
     }
     }
 
 
-    /// Return the ImplementationMode.
+    /// Return the interpolation mode.
     InterpolationMode GetInterpolationMode() const { return interpolationMode_; }
     InterpolationMode GetInterpolationMode() const { return interpolationMode_; }
 
 
-    /// Return the Knots of the Spline.
+    /// Return the knots of the spline.
     const VariantVector& GetKnots() const { return knots_; }
     const VariantVector& GetKnots() const { return knots_; }
 
 
-    /// Return the Knot at the specific index.
+    /// Return the knot at the specific index.
     Variant GetKnot(unsigned index) const { return knots_[index]; }
     Variant GetKnot(unsigned index) const { return knots_[index]; }
 
 
-    /// Return the T of the point of the Spline at f from 0.f - 1.f.
+    /// Return the T of the point of the spline at f from 0.f - 1.f.
     Variant GetPoint(float f) const;
     Variant GetPoint(float f) const;
 
 
-    /// Set the InterpolationMode of the Spline.
+    /// Set the interpolation mode.
     void SetInterpolationMode(InterpolationMode interpolationMode) { interpolationMode_ = interpolationMode; }
     void SetInterpolationMode(InterpolationMode interpolationMode) { interpolationMode_ = interpolationMode; }
 
 
-    /// Set the Knots of the Spline.
+    /// Set the knots of the spline.
     void SetKnots(const Vector<Variant>& knots) { knots_ = knots; }
     void SetKnots(const Vector<Variant>& knots) { knots_ = knots; }
 
 
-    /// Set the Knot value of an existing Knot.
+    /// Set the value of an existing knot.
     void SetKnot(const Variant& knot, unsigned index);
     void SetKnot(const Variant& knot, unsigned index);
-    /// Add a Knot to the end of the Spline.
+    /// Add a knot to the end of the spline.
     void AddKnot(const Variant& knot);
     void AddKnot(const Variant& knot);
-    /// Add a Knot to the Spline at a specific index.
+    /// Add a knot to the spline at a specific index.
     void AddKnot(const Variant& knot, unsigned index);
     void AddKnot(const Variant& knot, unsigned index);
 
 
-    /// Remove the last Knot on the Spline.
+    /// Remove the last knot on the spline.
     void RemoveKnot() { knots_.Pop(); }
     void RemoveKnot() { knots_.Pop(); }
 
 
-    /// Remove the Knot at the specific index.
+    /// Remove the knot at the specific index.
     void RemoveKnot(unsigned index) { knots_.Erase(index); }
     void RemoveKnot(unsigned index) { knots_.Erase(index); }
 
 
-    /// Clear the Spline.
+    /// Clear the spline.
     void Clear() { knots_.Clear(); }
     void Clear() { knots_.Clear(); }
 
 
 private:
 private:
-    /// Perform Bezier Interpolation on the Spline.
+    /// Perform Bezier interpolation on the spline.
     Variant BezierInterpolation(const Vector<Variant>& knots, float t) const;
     Variant BezierInterpolation(const Vector<Variant>& knots, float t) const;
-    /// LinearInterpolation between two Variants based on underlying type.
+    /// Perform Spline interpolation on the spline.
+    Variant CatmullRomInterpolation(const Vector<Variant>& knots, float t) const;
+    /// Perform linear interpolation on the spline.
+    Variant LinearInterpolation(const Vector<Variant>& knots, float t) const;
+    /// Linear interpolation between two Variants based on underlying type.
     Variant LinearInterpolation(const Variant& lhs, const Variant& rhs, float t) const;
     Variant LinearInterpolation(const Variant& lhs, const Variant& rhs, float t) const;
 
 
-    /// InterpolationMode.
+    /// Interpolation mode.
     InterpolationMode interpolationMode_;
     InterpolationMode interpolationMode_;
-    /// Knots on the Spline.
+    /// Knots on the spline.
     VariantVector knots_;
     VariantVector knots_;
 };
 };
 
 

+ 73 - 18
Source/Atomic/Core/Variant.cpp

@@ -23,6 +23,7 @@
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
 #include "../Core/StringUtils.h"
 #include "../Core/StringUtils.h"
+#include "../IO/VectorBuffer.h"
 
 
 #include <cstring>
 #include <cstring>
 
 
@@ -35,6 +36,7 @@ const ResourceRef Variant::emptyResourceRef;
 const ResourceRefList Variant::emptyResourceRefList;
 const ResourceRefList Variant::emptyResourceRefList;
 const VariantMap Variant::emptyVariantMap;
 const VariantMap Variant::emptyVariantMap;
 const VariantVector Variant::emptyVariantVector;
 const VariantVector Variant::emptyVariantVector;
+const StringVector Variant::emptyStringVector;
 
 
 static const char* typeNames[] =
 static const char* typeNames[] =
 {
 {
@@ -61,6 +63,7 @@ static const char* typeNames[] =
     "Matrix3x4",
     "Matrix3x4",
     "Matrix4",
     "Matrix4",
     "Double",
     "Double",
+    "StringVector",
     0
     0
 };
 };
 
 
@@ -90,6 +93,10 @@ Variant& Variant::operator =(const Variant& rhs)
         *(reinterpret_cast<VariantVector*>(&value_)) = *(reinterpret_cast<const VariantVector*>(&rhs.value_));
         *(reinterpret_cast<VariantVector*>(&value_)) = *(reinterpret_cast<const VariantVector*>(&rhs.value_));
         break;
         break;
 
 
+    case VAR_STRINGVECTOR:
+        *(reinterpret_cast<StringVector*>(&value_)) = *(reinterpret_cast<const StringVector*>(&rhs.value_));
+        break;
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         *(reinterpret_cast<VariantMap*>(&value_)) = *(reinterpret_cast<const VariantMap*>(&rhs.value_));
         *(reinterpret_cast<VariantMap*>(&value_)) = *(reinterpret_cast<const VariantMap*>(&rhs.value_));
         break;
         break;
@@ -118,6 +125,13 @@ Variant& Variant::operator =(const Variant& rhs)
     return *this;
     return *this;
 }
 }
 
 
+Variant& Variant::operator =(const VectorBuffer& rhs)
+{
+    SetType(VAR_BUFFER);
+    *(reinterpret_cast<PODVector<unsigned char>*>(&value_)) = rhs.GetBuffer();
+    return *this;
+}
+
 bool Variant::operator ==(const Variant& rhs) const
 bool Variant::operator ==(const Variant& rhs) const
 {
 {
     if (type_ == VAR_VOIDPTR || type_ == VAR_PTR)
     if (type_ == VAR_VOIDPTR || type_ == VAR_PTR)
@@ -164,6 +178,9 @@ bool Variant::operator ==(const Variant& rhs) const
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         return *(reinterpret_cast<const VariantVector*>(&value_)) == *(reinterpret_cast<const VariantVector*>(&rhs.value_));
         return *(reinterpret_cast<const VariantVector*>(&value_)) == *(reinterpret_cast<const VariantVector*>(&rhs.value_));
 
 
+    case VAR_STRINGVECTOR:
+        return *(reinterpret_cast<const StringVector*>(&value_)) == *(reinterpret_cast<const StringVector*>(&rhs.value_));
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         return *(reinterpret_cast<const VariantMap*>(&value_)) == *(reinterpret_cast<const VariantMap*>(&rhs.value_));
         return *(reinterpret_cast<const VariantMap*>(&value_)) == *(reinterpret_cast<const VariantMap*>(&rhs.value_));
 
 
@@ -190,6 +207,23 @@ bool Variant::operator ==(const Variant& rhs) const
     }
     }
 }
 }
 
 
+bool Variant::operator ==(const PODVector<unsigned char>& rhs) const
+{
+    // Use strncmp() instead of PODVector<unsigned char>::operator ==()
+    const PODVector<unsigned char>& buffer = *(reinterpret_cast<const PODVector<unsigned char>*>(&value_));
+    return type_ == VAR_BUFFER && buffer.Size() == rhs.Size() ?
+        strncmp(reinterpret_cast<const char*>(&buffer[0]), reinterpret_cast<const char*>(&rhs[0]), buffer.Size()) == 0 :
+        false;
+}
+
+bool Variant::operator ==(const VectorBuffer& rhs) const
+{
+    const PODVector<unsigned char>& buffer = *(reinterpret_cast<const PODVector<unsigned char>*>(&value_));
+    return type_ == VAR_BUFFER && buffer.Size() == rhs.GetSize() ?
+        strncmp(reinterpret_cast<const char*>(&buffer[0]), reinterpret_cast<const char*>(rhs.GetData()), buffer.Size()) == 0 :
+        false;
+}
+
 void Variant::FromString(const String& type, const String& value)
 void Variant::FromString(const String& type, const String& value)
 {
 {
     return FromString(GetTypeFromName(type), value.CString());
     return FromString(GetTypeFromName(type), value.CString());
@@ -260,7 +294,7 @@ void Variant::FromString(VariantType type, const char* value)
 
 
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF:
     {
     {
-        Vector<String> values = String::Split(value, ';');
+        StringVector values = String::Split(value, ';');
         if (values.Size() == 2)
         if (values.Size() == 2)
         {
         {
             SetType(VAR_RESOURCEREF);
             SetType(VAR_RESOURCEREF);
@@ -273,7 +307,7 @@ void Variant::FromString(VariantType type, const char* value)
 
 
     case VAR_RESOURCEREFLIST:
     case VAR_RESOURCEREFLIST:
     {
     {
-        Vector<String> values = String::Split(value, ';');
+        StringVector values = String::Split(value, ';');
         if (values.Size() >= 1)
         if (values.Size() >= 1)
         {
         {
             SetType(VAR_RESOURCEREFLIST);
             SetType(VAR_RESOURCEREFLIST);
@@ -310,7 +344,7 @@ void Variant::FromString(VariantType type, const char* value)
     case VAR_MATRIX4:
     case VAR_MATRIX4:
         *this = ToMatrix4(value);
         *this = ToMatrix4(value);
         break;
         break;
-        
+
     case VAR_DOUBLE:
     case VAR_DOUBLE:
         *this = ToDouble(value);
         *this = ToDouble(value);
         break;
         break;
@@ -332,6 +366,11 @@ void Variant::SetBuffer(const void* data, unsigned size)
         memcpy(&buffer[0], data, size);
         memcpy(&buffer[0], data, size);
 }
 }
 
 
+const VectorBuffer Variant::GetVectorBuffer() const
+{
+    return VectorBuffer(type_ == VAR_BUFFER ? *reinterpret_cast<const PODVector<unsigned char>*>(&value_) : emptyBuffer);
+}
+
 String Variant::GetTypeName() const
 String Variant::GetTypeName() const
 {
 {
     return typeNames[type_];
     return typeNames[type_];
@@ -369,12 +408,12 @@ String Variant::ToString() const
         return *(reinterpret_cast<const String*>(&value_));
         return *(reinterpret_cast<const String*>(&value_));
 
 
     case VAR_BUFFER:
     case VAR_BUFFER:
-    {
-        const PODVector<unsigned char>& buffer = *(reinterpret_cast<const PODVector<unsigned char>*>(&value_));
-        String ret;
-        BufferToString(ret, buffer.Begin().ptr_, buffer.Size());
-        return ret;
-    }
+        {
+            const PODVector<unsigned char>& buffer = *(reinterpret_cast<const PODVector<unsigned char>*>(&value_));
+            String ret;
+            BufferToString(ret, buffer.Begin().ptr_, buffer.Size());
+            return ret;
+        }
 
 
     case VAR_VOIDPTR:
     case VAR_VOIDPTR:
     case VAR_PTR:
     case VAR_PTR:
@@ -387,12 +426,6 @@ String Variant::ToString() const
     case VAR_INTVECTOR2:
     case VAR_INTVECTOR2:
         return (reinterpret_cast<const IntVector2*>(&value_))->ToString();
         return (reinterpret_cast<const IntVector2*>(&value_))->ToString();
 
 
-    default:
-        // VAR_RESOURCEREF, VAR_RESOURCEREFLIST, VAR_VARIANTVECTOR, VAR_VARIANTMAP
-        // Reference string serialization requires typehash-to-name mapping from the context. Can not support here
-        // Also variant map or vector string serialization is not supported. XML or binary save should be used instead
-        return String::EMPTY;
-
     case VAR_MATRIX3:
     case VAR_MATRIX3:
         return (reinterpret_cast<const Matrix3*>(value_.ptr_))->ToString();
         return (reinterpret_cast<const Matrix3*>(value_.ptr_))->ToString();
 
 
@@ -404,6 +437,12 @@ String Variant::ToString() const
 
 
     case VAR_DOUBLE:
     case VAR_DOUBLE:
         return String(*reinterpret_cast<const double*>(&value_));
         return String(*reinterpret_cast<const double*>(&value_));
+
+    default:
+        // VAR_RESOURCEREF, VAR_RESOURCEREFLIST, VAR_VARIANTVECTOR, VAR_STRINGVECTOR, VAR_VARIANTMAP
+        // Reference string serialization requires typehash-to-name mapping from the context. Can not support here
+        // Also variant map or vector string serialization is not supported. XML or binary save should be used instead
+        return String::EMPTY;
     }
     }
 }
 }
 
 
@@ -450,8 +489,8 @@ bool Variant::IsZero() const
 
 
     case VAR_RESOURCEREFLIST:
     case VAR_RESOURCEREFLIST:
     {
     {
-        const Vector<String>& names = reinterpret_cast<const ResourceRefList*>(&value_)->names_;
-        for (Vector<String>::ConstIterator i = names.Begin(); i != names.End(); ++i)
+        const StringVector& names = reinterpret_cast<const ResourceRefList*>(&value_)->names_;
+        for (StringVector::ConstIterator i = names.Begin(); i != names.End(); ++i)
         {
         {
             if (!i->Empty())
             if (!i->Empty())
                 return false;
                 return false;
@@ -462,6 +501,9 @@ bool Variant::IsZero() const
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         return reinterpret_cast<const VariantVector*>(&value_)->Empty();
         return reinterpret_cast<const VariantVector*>(&value_)->Empty();
 
 
+    case VAR_STRINGVECTOR:
+        return reinterpret_cast<const StringVector*>(&value_)->Empty();
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         return reinterpret_cast<const VariantMap*>(&value_)->Empty();
         return reinterpret_cast<const VariantMap*>(&value_)->Empty();
 
 
@@ -482,7 +524,7 @@ bool Variant::IsZero() const
 
 
     case VAR_MATRIX4:
     case VAR_MATRIX4:
         return *reinterpret_cast<const Matrix4*>(value_.ptr_) == Matrix4::IDENTITY;
         return *reinterpret_cast<const Matrix4*>(value_.ptr_) == Matrix4::IDENTITY;
-        
+
     case VAR_DOUBLE:
     case VAR_DOUBLE:
         return *reinterpret_cast<const double*>(&value_) == 0.0;
         return *reinterpret_cast<const double*>(&value_) == 0.0;
 
 
@@ -518,6 +560,10 @@ void Variant::SetType(VariantType newType)
         (reinterpret_cast<VariantVector*>(&value_))->~VariantVector();
         (reinterpret_cast<VariantVector*>(&value_))->~VariantVector();
         break;
         break;
 
 
+    case VAR_STRINGVECTOR:
+        (reinterpret_cast<StringVector*>(&value_))->~StringVector();
+        break;
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         (reinterpret_cast<VariantMap*>(&value_))->~VariantMap();
         (reinterpret_cast<VariantMap*>(&value_))->~VariantMap();
         break;
         break;
@@ -566,6 +612,10 @@ void Variant::SetType(VariantType newType)
         new(reinterpret_cast<VariantVector*>(&value_)) VariantVector();
         new(reinterpret_cast<VariantVector*>(&value_)) VariantVector();
         break;
         break;
 
 
+    case VAR_STRINGVECTOR:
+        new(reinterpret_cast<StringVector*>(&value_)) StringVector();
+        break;
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         new(reinterpret_cast<VariantMap*>(&value_)) VariantMap();
         new(reinterpret_cast<VariantMap*>(&value_)) VariantMap();
         break;
         break;
@@ -706,6 +756,11 @@ template <> VariantVector Variant::Get<VariantVector>() const
     return GetVariantVector();
     return GetVariantVector();
 }
 }
 
 
+template <> StringVector Variant::Get<StringVector >() const
+{
+    return GetStringVector();
+}
+
 template <> VariantMap Variant::Get<VariantMap>() const
 template <> VariantMap Variant::Get<VariantMap>() const
 {
 {
     return GetVariantMap();
     return GetVariantMap();

+ 120 - 23
Source/Atomic/Core/Variant.h

@@ -59,10 +59,11 @@ enum VariantType
     VAR_MATRIX3X4,
     VAR_MATRIX3X4,
     VAR_MATRIX4,
     VAR_MATRIX4,
     VAR_DOUBLE,
     VAR_DOUBLE,
+    VAR_STRINGVECTOR,
     MAX_VAR_TYPES
     MAX_VAR_TYPES
 };
 };
 
 
-/// Union for the possible variant values. Also stores non-POD objects such as String which must not exceed 16 bytes in size.
+/// Union for the possible variant values. Also stores non-POD objects such as String and math objects (excluding Matrix) which must not exceed 16 bytes in size. Objects exceeding 16 bytes size are stored in the heap pointed by _ptr.
 struct VariantValue
 struct VariantValue
 {
 {
     union
     union
@@ -95,6 +96,18 @@ struct VariantValue
     };
     };
 };
 };
 
 
+class Variant;
+class VectorBuffer;
+
+/// Vector of variants.
+typedef Vector<Variant> VariantVector;
+
+/// Vector of strings.
+typedef Vector<String> StringVector;
+
+/// Map of variants.
+typedef HashMap<StringHash, Variant> VariantMap;
+
 /// Typed resource reference.
 /// Typed resource reference.
 struct ATOMIC_API ResourceRef
 struct ATOMIC_API ResourceRef
 {
 {
@@ -150,7 +163,7 @@ struct ATOMIC_API ResourceRefList
     }
     }
 
 
     /// Construct with type and id list.
     /// Construct with type and id list.
-    ResourceRefList(StringHash type, const Vector<String>& names) :
+    ResourceRefList(StringHash type, const StringVector& names) :
         type_(type),
         type_(type),
         names_(names)
         names_(names)
     {
     {
@@ -159,7 +172,7 @@ struct ATOMIC_API ResourceRefList
     /// Object type.
     /// Object type.
     StringHash type_;
     StringHash type_;
     /// List of object names.
     /// List of object names.
-    Vector<String> names_;
+    StringVector names_;
 
 
     /// Test for equality with another reference list.
     /// Test for equality with another reference list.
     bool operator ==(const ResourceRefList& rhs) const { return type_ == rhs.type_ && names_ == rhs.names_; }
     bool operator ==(const ResourceRefList& rhs) const { return type_ == rhs.type_ && names_ == rhs.names_; }
@@ -168,14 +181,6 @@ struct ATOMIC_API ResourceRefList
     bool operator !=(const ResourceRefList& rhs) const { return type_ != rhs.type_ || names_ != rhs.names_; }
     bool operator !=(const ResourceRefList& rhs) const { return type_ != rhs.type_ || names_ != rhs.names_; }
 };
 };
 
 
-class Variant;
-
-/// Vector of variants.
-typedef Vector<Variant> VariantVector;
-
-/// Map of variants.
-typedef HashMap<StringHash, Variant> VariantMap;
-
 /// Variable that supports a fixed set of types.
 /// Variable that supports a fixed set of types.
 class ATOMIC_API Variant
 class ATOMIC_API Variant
 {
 {
@@ -284,6 +289,13 @@ public:
         *this = value;
         *this = value;
     }
     }
 
 
+    /// Construct from a %VectorBuffer and store as a buffer.
+    Variant(const VectorBuffer& value) :
+        type_(VAR_NONE)
+    {
+        *this = value;
+    }
+
     /// Construct from a pointer.
     /// Construct from a pointer.
     Variant(void* value) :
     Variant(void* value) :
         type_(VAR_NONE)
         type_(VAR_NONE)
@@ -319,6 +331,13 @@ public:
         *this = value;
         *this = value;
     }
     }
 
 
+    /// Construct from a string vector.
+    Variant(const StringVector& value) :
+        type_ (VAR_NONE)
+    {
+        *this = value;
+    }
+
     /// Construct from an integer rect.
     /// Construct from an integer rect.
     Variant(const IntRect& value) :
     Variant(const IntRect& value) :
         type_(VAR_NONE)
         type_(VAR_NONE)
@@ -523,6 +542,9 @@ public:
         return *this;
         return *this;
     }
     }
 
 
+    /// Assign from a %VectorBuffer and store as a buffer.
+    Variant& operator =(const VectorBuffer& rhs);
+
     /// Assign from a void pointer.
     /// Assign from a void pointer.
     Variant& operator =(void* rhs)
     Variant& operator =(void* rhs)
     {
     {
@@ -555,6 +577,14 @@ public:
         return *this;
         return *this;
     }
     }
 
 
+    // Assign from a string vector.
+    Variant& operator =(const StringVector& rhs)
+    {
+        SetType(VAR_STRINGVECTOR);
+        *(reinterpret_cast<StringVector*>(&value_)) = rhs;
+        return *this;
+    }
+
     /// Assign from a variant map.
     /// Assign from a variant map.
     Variant& operator =(const VariantMap& rhs)
     Variant& operator =(const VariantMap& rhs)
     {
     {
@@ -666,10 +696,9 @@ public:
     }
     }
 
 
     /// Test for equality with a buffer. To return true, both the type and value must match.
     /// Test for equality with a buffer. To return true, both the type and value must match.
-    bool operator ==(const PODVector<unsigned char>& rhs) const
-    {
-        return type_ == VAR_BUFFER ? *(reinterpret_cast<const PODVector<unsigned char>*>(&value_)) == rhs : false;
-    }
+    bool operator ==(const PODVector<unsigned char>& rhs) const;
+    /// Test for equality with a %VectorBuffer. To return true, both the type and value must match.
+    bool operator ==(const VectorBuffer& rhs) const;
 
 
     /// Test for equality with a void pointer. To return true, both the type and value must match, with the exception that a RefCounted pointer is also allowed.
     /// Test for equality with a void pointer. To return true, both the type and value must match, with the exception that a RefCounted pointer is also allowed.
     bool operator ==(void* rhs) const
     bool operator ==(void* rhs) const
@@ -700,6 +729,12 @@ public:
         return type_ == VAR_VARIANTVECTOR ? *(reinterpret_cast<const VariantVector*>(&value_)) == rhs : false;
         return type_ == VAR_VARIANTVECTOR ? *(reinterpret_cast<const VariantVector*>(&value_)) == rhs : false;
     }
     }
 
 
+    /// Test for equality with a string vector. To return true, both the type and value must match.
+    bool operator ==(const StringVector& rhs) const
+    {
+        return type_ == VAR_STRINGVECTOR ? *(reinterpret_cast<const StringVector*>(&value_)) == rhs : false;
+    }
+
     /// Test for equality with a variant map. To return true, both the type and value must match.
     /// Test for equality with a variant map. To return true, both the type and value must match.
     bool operator ==(const VariantMap& rhs) const
     bool operator ==(const VariantMap& rhs) const
     {
     {
@@ -786,6 +821,9 @@ public:
     /// Test for inequality with a buffer.
     /// Test for inequality with a buffer.
     bool operator !=(const PODVector<unsigned char>& rhs) const { return !(*this == rhs); }
     bool operator !=(const PODVector<unsigned char>& rhs) const { return !(*this == rhs); }
 
 
+    /// Test for inequality with a %VectorBuffer.
+    bool operator !=(const VectorBuffer& rhs) const { return !(*this == rhs); }
+
     /// Test for inequality with a pointer.
     /// Test for inequality with a pointer.
     bool operator !=(void* rhs) const { return !(*this == rhs); }
     bool operator !=(void* rhs) const { return !(*this == rhs); }
 
 
@@ -798,6 +836,9 @@ public:
     /// Test for inequality with a variant vector.
     /// Test for inequality with a variant vector.
     bool operator !=(const VariantVector& rhs) const { return !(*this == rhs); }
     bool operator !=(const VariantVector& rhs) const { return !(*this == rhs); }
 
 
+    /// Test for inequality with a string vector.
+    bool operator !=(const StringVector& rhs) const { return !(*this == rhs); }
+
     /// Test for inequality with a variant map.
     /// Test for inequality with a variant map.
     bool operator !=(const VariantMap& rhs) const { return !(*this == rhs); }
     bool operator !=(const VariantMap& rhs) const { return !(*this == rhs); }
 
 
@@ -833,11 +874,31 @@ public:
     /// Set buffer type from a memory area.
     /// Set buffer type from a memory area.
     void SetBuffer(const void* data, unsigned size);
     void SetBuffer(const void* data, unsigned size);
 
 
-    /// Return int or zero on type mismatch.
-    int GetInt() const { return type_ == VAR_INT ? value_.int_ : 0; }
+    /// Return int or zero on type mismatch. Floats and doubles are converted.
+    int GetInt() const
+    {
+        if (type_ == VAR_INT)
+            return value_.int_;
+        else if (type_ == VAR_FLOAT)
+            return (int)value_.float_;
+        else if (type_ == VAR_DOUBLE)
+            return (int)*reinterpret_cast<const double*>(&value_);
+        else
+            return 0;
+    }
 
 
-    /// Return unsigned int or zero on type mismatch.
-    unsigned GetUInt() const { return type_ == VAR_INT ? (unsigned)value_.int_ : 0; }
+    /// Return unsigned int or zero on type mismatch. Floats and doubles are converted.
+    unsigned GetUInt() const
+    {
+        if (type_ == VAR_INT)
+            return value_.int_;
+        else if (type_ == VAR_FLOAT)
+            return (unsigned)value_.float_;
+        else if (type_ == VAR_DOUBLE)
+            return (unsigned)*reinterpret_cast<const double*>(&value_);
+        else
+            return 0;
+    }
 
 
     /// Return StringHash or zero on type mismatch.
     /// Return StringHash or zero on type mismatch.
     StringHash GetStringHash() const { return StringHash(GetUInt()); }
     StringHash GetStringHash() const { return StringHash(GetUInt()); }
@@ -845,11 +906,31 @@ public:
     /// Return bool or false on type mismatch.
     /// Return bool or false on type mismatch.
     bool GetBool() const { return type_ == VAR_BOOL ? value_.bool_ : false; }
     bool GetBool() const { return type_ == VAR_BOOL ? value_.bool_ : false; }
 
 
-    /// Return float or zero on type mismatch.
-    float GetFloat() const { return type_ == VAR_FLOAT ? value_.float_ : 0.0f; }
+    /// Return float or zero on type mismatch. Ints and doubles are converted.
+    float GetFloat() const
+    {
+        if (type_ == VAR_FLOAT)
+            return value_.float_;
+        else if (type_ == VAR_DOUBLE)
+            return (float)*reinterpret_cast<const double*>(&value_);
+        else if (type_ == VAR_INT)
+            return (float)value_.int_;
+        else
+            return 0.0f;
+    }
 
 
-    /// Return double or zero on type mismatch.
-    double GetDouble() const { return type_ == VAR_DOUBLE ? *reinterpret_cast<const double*>(&value_) : 0.0; }
+    /// Return double or zero on type mismatch. Ints and floats are converted.
+    double GetDouble() const
+    {
+        if (type_ == VAR_DOUBLE)
+            return *reinterpret_cast<const double*>(&value_);
+        else if (type_ == VAR_FLOAT)
+            return (double)value_.float_;
+        else if (type_ == VAR_INT)
+            return (double)value_.int_;
+        else
+            return 0.0;
+    }
 
 
     /// Return Vector2 or zero on type mismatch.
     /// Return Vector2 or zero on type mismatch.
     const Vector2& GetVector2() const { return type_ == VAR_VECTOR2 ? *reinterpret_cast<const Vector2*>(&value_) : Vector2::ZERO; }
     const Vector2& GetVector2() const { return type_ == VAR_VECTOR2 ? *reinterpret_cast<const Vector2*>(&value_) : Vector2::ZERO; }
@@ -878,6 +959,9 @@ public:
         return type_ == VAR_BUFFER ? *reinterpret_cast<const PODVector<unsigned char>*>(&value_) : emptyBuffer;
         return type_ == VAR_BUFFER ? *reinterpret_cast<const PODVector<unsigned char>*>(&value_) : emptyBuffer;
     }
     }
 
 
+    /// Return %VectorBuffer containing the buffer or empty on type mismatch.
+    const VectorBuffer GetVectorBuffer() const;
+
     /// Return void pointer or null on type mismatch. RefCounted pointer will be converted.
     /// Return void pointer or null on type mismatch. RefCounted pointer will be converted.
     void* GetVoidPtr() const
     void* GetVoidPtr() const
     {
     {
@@ -907,6 +991,12 @@ public:
         return type_ == VAR_VARIANTVECTOR ? *reinterpret_cast<const VariantVector*>(&value_) : emptyVariantVector;
         return type_ == VAR_VARIANTVECTOR ? *reinterpret_cast<const VariantVector*>(&value_) : emptyVariantVector;
     }
     }
 
 
+    /// Return a string vector or empty on type mismatch.
+    const StringVector& GetStringVector() const
+    {
+        return type_ == VAR_STRINGVECTOR ? *reinterpret_cast<const StringVector*>(&value_) : emptyStringVector;
+    }
+
     /// Return a variant map or empty on type mismatch.
     /// Return a variant map or empty on type mismatch.
     const VariantMap& GetVariantMap() const
     const VariantMap& GetVariantMap() const
     {
     {
@@ -971,6 +1061,9 @@ public:
     /// Return a pointer to a modifiable variant vector or null on type mismatch.
     /// Return a pointer to a modifiable variant vector or null on type mismatch.
     VariantVector* GetVariantVectorPtr() { return type_ == VAR_VARIANTVECTOR ? reinterpret_cast<VariantVector*>(&value_) : 0; }
     VariantVector* GetVariantVectorPtr() { return type_ == VAR_VARIANTVECTOR ? reinterpret_cast<VariantVector*>(&value_) : 0; }
 
 
+    /// Return a pointer to a modifiable string vector or null on type mismatch.
+    StringVector* GetStringVectorPtr() { return type_ == VAR_STRINGVECTOR ? reinterpret_cast<StringVector*>(&value_) : 0; }
+
     /// Return a pointer to a modifiable variant map or null on type mismatch.
     /// Return a pointer to a modifiable variant map or null on type mismatch.
     VariantMap* GetVariantMapPtr() { return type_ == VAR_VARIANTMAP ? reinterpret_cast<VariantMap*>(&value_) : 0; }
     VariantMap* GetVariantMapPtr() { return type_ == VAR_VARIANTMAP ? reinterpret_cast<VariantMap*>(&value_) : 0; }
 
 
@@ -993,6 +1086,8 @@ public:
     static const VariantMap emptyVariantMap;
     static const VariantMap emptyVariantMap;
     /// Empty variant vector.
     /// Empty variant vector.
     static const VariantVector emptyVariantVector;
     static const VariantVector emptyVariantVector;
+    /// Empty string vector.
+    static const StringVector emptyStringVector;
 
 
 private:
 private:
     /// Set new type and allocate/deallocate memory as necessary.
     /// Set new type and allocate/deallocate memory as necessary.
@@ -1040,6 +1135,8 @@ template <> inline VariantType GetVariantType<ResourceRefList>() { return VAR_RE
 
 
 template <> inline VariantType GetVariantType<VariantVector>() { return VAR_VARIANTVECTOR; }
 template <> inline VariantType GetVariantType<VariantVector>() { return VAR_VARIANTVECTOR; }
 
 
+template <> inline VariantType GetVariantType<StringVector >() { return VAR_STRINGVECTOR; }
+
 template <> inline VariantType GetVariantType<VariantMap>() { return VAR_VARIANTMAP; }
 template <> inline VariantType GetVariantType<VariantMap>() { return VAR_VARIANTMAP; }
 
 
 template <> inline VariantType GetVariantType<IntRect>() { return VAR_INTRECT; }
 template <> inline VariantType GetVariantType<IntRect>() { return VAR_INTRECT; }

+ 5 - 1
Source/Atomic/Core/WorkQueue.cpp

@@ -67,6 +67,7 @@ WorkQueue::WorkQueue(Context* context) :
     shutDown_(false),
     shutDown_(false),
     pausing_(false),
     pausing_(false),
     paused_(false),
     paused_(false),
+    completing_(false),
     tolerance_(10),
     tolerance_(10),
     lastSize_(0),
     lastSize_(0),
     maxNonThreadedWorkMs_(5)
     maxNonThreadedWorkMs_(5)
@@ -234,6 +235,8 @@ void WorkQueue::Resume()
 
 
 void WorkQueue::Complete(unsigned priority)
 void WorkQueue::Complete(unsigned priority)
 {
 {
+    completing_ = true;
+
     if (threads_.Size())
     if (threads_.Size())
     {
     {
         Resume();
         Resume();
@@ -279,6 +282,7 @@ void WorkQueue::Complete(unsigned priority)
     }
     }
 
 
     PurgeCompleted(priority);
     PurgeCompleted(priority);
+    completing_ = false;
 }
 }
 
 
 bool WorkQueue::IsCompleted(unsigned priority) const
 bool WorkQueue::IsCompleted(unsigned priority) const
@@ -370,7 +374,7 @@ void WorkQueue::ReturnToPool(SharedPtr<WorkItem>& item)
     // Check if this was a pooled item and set it to usable
     // Check if this was a pooled item and set it to usable
     if (item->pooled_)
     if (item->pooled_)
     {
     {
-        // Reset the values to their defaults. This should 
+        // Reset the values to their defaults. This should
         // be safe to do here as the completed event has
         // be safe to do here as the completed event has
         // already been handled and this is part of the
         // already been handled and this is part of the
         // internal pool.
         // internal pool.

+ 4 - 0
Source/Atomic/Core/WorkQueue.h

@@ -114,6 +114,8 @@ public:
 
 
     /// Return whether all work with at least the specified priority is finished.
     /// Return whether all work with at least the specified priority is finished.
     bool IsCompleted(unsigned priority) const;
     bool IsCompleted(unsigned priority) const;
+    /// Return whether the queue is currently completing work in the main thread.
+    bool IsCompleting() const { return completing_; }
 
 
     /// Return the pool tolerance.
     /// Return the pool tolerance.
     int GetTolerance() const { return tolerance_; }
     int GetTolerance() const { return tolerance_; }
@@ -149,6 +151,8 @@ private:
     volatile bool pausing_;
     volatile bool pausing_;
     /// Paused flag. Indicates the queue mutex being locked to prevent worker threads using up CPU time.
     /// Paused flag. Indicates the queue mutex being locked to prevent worker threads using up CPU time.
     bool paused_;
     bool paused_;
+    /// Completing work in the main thread flag.
+    bool completing_;
     /// Tolerance for the shared pool before it begins to deallocate.
     /// Tolerance for the shared pool before it begins to deallocate.
     int tolerance_;
     int tolerance_;
     /// Last size of the shared pool.
     /// Last size of the shared pool.

+ 99 - 0
Source/Atomic/Database/Database.cpp

@@ -0,0 +1,99 @@
+//
+// 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 "../Precompiled.h"
+
+#include "../Core/Profiler.h"
+#include "../Database/Database.h"
+
+namespace Urho3D
+{
+
+Database::Database(Context* context_) :
+    Object(context_),
+#ifdef ODBC_3_OR_LATER
+    poolSize_(0)
+#else
+    poolSize_(M_MAX_UNSIGNED)
+#endif
+{
+}
+
+DBAPI Database::GetAPI()
+{
+#ifdef URHO3D_DATABASE_ODBC
+    return DBAPI_ODBC;
+#else
+    return DBAPI_SQLITE;
+#endif
+}
+
+DbConnection* Database::Connect(const String& connectionString)
+{
+    PROFILE(DatabaseConnect);
+
+    SharedPtr<DbConnection> connection;
+    if (IsPooling())
+    {
+        Vector<SharedPtr<DbConnection> >& connectionsPool = connectionsPool_[connectionString];
+        while (!connectionsPool.Empty())
+        {
+            connection = connectionsPool.Back();
+            connectionsPool.Pop();
+            if (connection->IsConnected())
+                break;
+            connection = 0;
+        }
+    }
+    if (!connection)
+        connection = new DbConnection(context_, connectionString);
+    if (connection->IsConnected())
+    {
+        connections_.Push(connection);
+        return connection;
+    }
+    else
+        return 0;
+}
+
+void Database::Disconnect(DbConnection* connection)
+{
+    if (!connection)
+        return;
+
+    PROFILE(DatabaseDisconnect);
+
+    SharedPtr<DbConnection> dbConnection(connection);
+    connections_.Remove(dbConnection);
+
+    // Must finalize the connection before closing the connection or returning it to the pool
+    connection->Finalize();
+
+    if (IsPooling())
+    {
+        Vector<SharedPtr<DbConnection> >& connectionsPool = connectionsPool_[connection->GetConnectionString()];
+        if (connectionsPool.Size() < poolSize_)
+            connectionsPool.Push(dbConnection);
+    }
+}
+
+}

+ 74 - 0
Source/Atomic/Database/Database.h

@@ -0,0 +1,74 @@
+//
+// 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"
+#include "../Database/DbConnection.h"
+
+namespace Urho3D
+{
+
+/// Supported database API.
+enum DBAPI
+{
+    DBAPI_SQLITE = 0,
+    DBAPI_ODBC
+};
+
+class DbConnection;
+
+/// %Database subsystem. Manage database connections.
+class URHO3D_API Database : public Object
+{
+    OBJECT(Database);
+
+public:
+    /// Construct.
+    Database(Context* context_);
+    /// Return the underlying database API.
+    static DBAPI GetAPI();
+
+    /// Create new database connection. Return 0 if failed.
+    DbConnection* Connect(const String& connectionString);
+    /// Disconnect a database connection. The connection object pointer should not be used anymore after this.
+    void Disconnect(DbConnection* connection);
+
+    /// Return true when using internal database connection pool. The internal database pool is managed by the Database subsystem itself and should not be confused with ODBC connection pool option when ODBC is being used.
+    bool IsPooling() const { return (bool)poolSize_; }
+
+    /// Get internal database connection pool size.
+    unsigned GetPoolSize() const { return poolSize_; }
+
+    /// Set internal database connection pool size.
+    void SetPoolSize(unsigned poolSize) { poolSize_ = poolSize; }
+
+private:
+    /// %Database connection pool size. Default to 0 when using ODBC 3.0 or later as ODBC 3.0 driver manager could manage its own database connection pool.
+    unsigned poolSize_;
+    /// Active database connections.
+    Vector<SharedPtr<DbConnection> > connections_;
+    ///%Database connections pool.
+    HashMap<String, Vector<SharedPtr<DbConnection> > > connectionsPool_;
+};
+
+}

+ 44 - 0
Source/Atomic/Database/DatabaseEvents.h

@@ -0,0 +1,44 @@
+//
+// 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"
+#include "../Database/DbConnection.h"
+
+namespace Urho3D
+{
+
+/// %Database cursor. Event handler could set P_FILTER to true to filter out a row from resultset and P_ABORT to true to stop further cursor events.
+EVENT(E_DBCURSOR, DbCursor)
+{
+    PARAM(P_DBCONNECTION, DbConnection);    // DbConnection pointer
+    PARAM(P_RESULTIMPL, ResultImpl);        // Underlying result object pointer (cannot be used in scripting)
+    PARAM(P_SQL, SQL);                      // String
+    PARAM(P_NUMCOLS, NumCols);              // unsigned
+    PARAM(P_COLVALUES, ColValues);          // VariantVector
+    PARAM(P_COLHEADERS, ColHeaders);        // StringVector
+    PARAM(P_FILTER, Filter);                // bool [in]
+    PARAM(P_ABORT, Abort);                  // bool [in]
+}
+
+}

+ 31 - 0
Source/Atomic/Database/DbConnection.h

@@ -0,0 +1,31 @@
+//
+// 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
+
+#ifdef URHO3D_DATABASE_ODBC
+#include "ODBC/ODBCConnection.h"
+#elif URHO3D_DATABASE_SQLITE
+#include "SQLite/SQLiteConnection.h"
+#else
+#error "Database subsystem not enabled"
+#endif

+ 31 - 0
Source/Atomic/Database/DbResult.h

@@ -0,0 +1,31 @@
+//
+// 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
+
+#ifdef URHO3D_DATABASE_ODBC
+#include "ODBC/ODBCResult.h"
+#elif URHO3D_DATABASE_SQLITE
+#include "SQLite/SQLiteResult.h"
+#else
+#error "Database subsystem not enabled"
+#endif

+ 158 - 0
Source/Atomic/Database/ODBC/ODBCConnection.cpp

@@ -0,0 +1,158 @@
+//
+// 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 "../../Precompiled.h"
+
+#include "../../Database/DatabaseEvents.h"
+#include "../../IO/Log.h"
+
+#include <sqlext.h>
+
+namespace Urho3D
+{
+
+DbConnection::DbConnection(Context* context, const String& connectionString) :
+    Object(context),
+    connectionString_(connectionString)
+{
+    try
+    {
+        connectionImpl_ = nanodbc::connection(connectionString.CString());
+    }
+    catch (std::runtime_error& e)
+    {
+        HandleRuntimeError("Could not connect", e.what());
+    }
+}
+
+DbConnection::~DbConnection()
+{
+    try
+    {
+        Finalize();
+        connectionImpl_.disconnect();
+    }
+    catch (std::runtime_error& e)
+    {
+        // This should not happen after finalizing the connection, log error in Release but assert in Debug
+        HandleRuntimeError("Could not disconnect", e.what());
+        assert(false);
+    }
+}
+
+void DbConnection::Finalize()
+{
+    // TODO
+}
+
+DbResult DbConnection::Execute(const String& sql, bool useCursorEvent)
+{
+    DbResult result;
+
+    try
+    {
+        result.resultImpl_ = nanodbc::execute(connectionImpl_, sql.Trimmed().CString());
+        unsigned numCols = (unsigned)result.resultImpl_.columns();
+        if (numCols)
+        {
+            result.columns_.Resize(numCols);
+            for (unsigned i = 0; i < numCols; ++i)
+                result.columns_[i] = result.resultImpl_.column_name((short)i).c_str();
+
+            bool filtered = false;
+            bool aborted = false;
+
+            while (result.resultImpl_.next())
+            {
+                VariantVector colValues(numCols);
+                for (unsigned i = 0; i < numCols; ++i)
+                {
+                    if (!result.resultImpl_.is_null((short)i))
+                    {
+                        // We can only bind primitive data type that our Variant class supports
+                        switch (result.resultImpl_.column_c_datatype((short)i))
+                        {
+                        case SQL_C_LONG:
+                            colValues[i] = result.resultImpl_.get<int>((short)i);
+                            if (result.resultImpl_.column_datatype((short)i) == SQL_BIT)
+                                colValues[i] = colValues[i] != 0;
+                            break;
+
+                        case SQL_C_FLOAT:
+                            colValues[i] = result.resultImpl_.get<float>((short)i);
+                            break;
+
+                        case SQL_C_DOUBLE:
+                            colValues[i] = result.resultImpl_.get<double>((short)i);
+                            break;
+
+                        default:
+                            // All other types are stored using their string representation in the Variant
+                            colValues[i] = result.resultImpl_.get<nanodbc::string_type>((short)i).c_str();
+                            break;
+                        }
+                    }
+                }
+
+                if (useCursorEvent)
+                {
+                    using namespace DbCursor;
+
+                    VariantMap& eventData = GetEventDataMap();
+                    eventData[P_DBCONNECTION] = this;
+                    eventData[P_RESULTIMPL] = &result.resultImpl_;
+                    eventData[P_SQL] = sql;
+                    eventData[P_NUMCOLS] = numCols;
+                    eventData[P_COLVALUES] = colValues;
+                    eventData[P_COLHEADERS] = result.columns_;
+                    eventData[P_FILTER] = false;
+                    eventData[P_ABORT] = false;
+
+                    SendEvent(E_DBCURSOR, eventData);
+
+                    filtered = eventData[P_FILTER].GetBool();
+                    aborted = eventData[P_ABORT].GetBool();
+                }
+
+                if (!filtered)
+                    result.rows_.Push(colValues);
+                if (aborted)
+                    break;
+            }
+        }
+        result.numAffectedRows_ = numCols ? -1 : result.resultImpl_.affected_rows();
+    }
+    catch (std::runtime_error& e)
+    {
+        HandleRuntimeError("Could not execute", e.what());
+    }
+
+    return result;
+}
+
+void DbConnection::HandleRuntimeError(const char* message, const char* cause)
+{
+    StringVector tokens = (String(cause) + "::").Split(':');      // Added "::" as sentinels against unexpected cause format
+    LOGERRORF("%s: nanodbc:%s:%s", message, tokens[1].CString(), tokens[2].CString());
+}
+
+}

+ 68 - 0
Source/Atomic/Database/ODBC/ODBCConnection.h

@@ -0,0 +1,68 @@
+//
+// 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"
+#include "../../Database/DbResult.h"
+
+#include <nanodbc/nanodbc.h>
+
+namespace Urho3D
+{
+
+/// %Database connection.
+class URHO3D_API DbConnection : public Object
+{
+    OBJECT(DbConnection);
+
+public:
+    /// Construct.
+    DbConnection(Context* context, const String& connectionString);
+    /// Destruct.
+    ~DbConnection();
+    /// Finalize all prepared statements, close all BLOB handles, and finish all sqlite3_backup objects
+    void Finalize();
+
+    /// Execute an SQL statements immediately. Send E_DBCURSOR event for each row in the resultset when useCursorEvent parameter is set to true.
+    DbResult Execute(const String& sql, bool useCursorEvent = false);
+
+    /// Return database connection string. The connection string for SQLite3 is using the URI format described in https://www.sqlite.org/uri.html, while the connection string for ODBC is using DSN format as per ODBC standard.
+    const String& GetConnectionString() const { return connectionString_; }
+
+    /// Return the underlying implementation connection object pointer. It is sqlite* when using SQLite3 or nanodbc::connection* when using ODBC.
+    const nanodbc::connection* GetConnectionImpl() const { return &connectionImpl_; }
+
+    /// Return true when the connection object is connected to the associated database.
+    bool IsConnected() const { return connectionImpl_.connected(); }
+
+private:
+    /// Internal helper method to handle runtime exception by logging it to stderr stream.
+    void HandleRuntimeError(const char* message, const char* cause);
+
+    /// The connection string for SQLite3 is using the URI format described in https://www.sqlite.org/uri.html, while the connection string for ODBC is using DSN format as per ODBC standard.
+    String connectionString_;
+    /// The underlying implementation connection object.
+    nanodbc::connection connectionImpl_;
+};
+
+}

+ 73 - 0
Source/Atomic/Database/ODBC/ODBCResult.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 "../../Core/Variant.h"
+
+#include <nanodbc/nanodbc.h>
+
+namespace Urho3D
+{
+
+/// %Database query result.
+class URHO3D_API DbResult
+{
+    friend class DbConnection;
+
+public:
+    // Default constructor constructs an empty result object.
+    DbResult() :
+        numAffectedRows_(-1)
+    {
+    }
+
+    /// Return number of columns in the resultset or 0 if there is no resultset.
+    unsigned GetNumColumns() const { return columns_.Size(); }
+
+    /// Return number of rows in the resultset or 0 if the number of rows is not available.
+    unsigned GetNumRows() const { return rows_.Size(); }
+
+    /// Return number of affected rows by the DML query or -1 if the number of affected rows is not available.
+    long GetNumAffectedRows() const { return numAffectedRows_; }
+
+    /// Return the underlying implementation result object.
+    const nanodbc::result& GetResultImpl() const { return resultImpl_; }
+
+    /// Return the column headers string collection.
+    const StringVector& GetColumns() const { return columns_; }
+
+    /// Return fetched rows collection. Filtered rows are not included in the collection.
+    const Vector<VariantVector>& GetRows() const { return rows_; }
+
+private:
+    /// The underlying implementation connection object.
+    nanodbc::result resultImpl_;
+    /// Column headers from the resultset.
+    StringVector columns_;
+    /// Fetched rows from the resultset.
+    Vector<VariantVector> rows_;
+    /// Number of affected rows by recent DML query.
+    long numAffectedRows_;
+};
+
+}

+ 162 - 0
Source/Atomic/Database/SQLite/SQLiteConnection.cpp

@@ -0,0 +1,162 @@
+//
+// 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 "../../Precompiled.h"
+
+#include "../../Database/DatabaseEvents.h"
+#include "../../IO/Log.h"
+
+namespace Urho3D
+{
+
+DbConnection::DbConnection(Context* context, const String& connectionString) :
+    Object(context),
+    connectionString_(connectionString),
+    connectionImpl_(0)
+{
+    if (sqlite3_open(connectionString.CString(), &connectionImpl_) != SQLITE_OK)
+    {
+        LOGERRORF("Could not connect: %s", sqlite3_errmsg(connectionImpl_));
+        sqlite3_close(connectionImpl_);
+        connectionImpl_ = 0;
+    }
+}
+
+DbConnection::~DbConnection()
+{
+    Finalize();
+    if (sqlite3_close(connectionImpl_) != SQLITE_OK)
+    {
+        // This should not happen after finalizing the connection, log error in Release but assert in Debug
+        LOGERRORF("Could not disconnect: %s", sqlite3_errmsg(connectionImpl_));
+        assert(false);
+    }
+    connectionImpl_ = 0;
+}
+
+void DbConnection::Finalize()
+{
+    // TODO
+}
+
+DbResult DbConnection::Execute(const String& sql, bool useCursorEvent)
+{
+    DbResult result;
+    const char* zLeftover = 0;
+    sqlite3_stmt* pStmt = 0;
+    assert(connectionImpl_);
+    int rc = sqlite3_prepare_v2(connectionImpl_, sql.Trimmed().CString(), -1, &pStmt, &zLeftover);
+    if (rc != SQLITE_OK)
+    {
+        LOGERRORF("Could not execute: %s", sqlite3_errmsg(connectionImpl_));
+        assert(!pStmt);
+        return result;
+    }
+    if (*zLeftover)
+    {
+        LOGERROR("Could not execute: only one SQL statement is allowed");
+        sqlite3_finalize(pStmt);
+        return result;
+    }
+
+    unsigned numCols = (unsigned)sqlite3_column_count(pStmt);
+    result.columns_.Resize(numCols);
+    for (unsigned i = 0; i < numCols; ++i)
+        result.columns_[i] = sqlite3_column_name(pStmt, i);
+
+    bool filtered = false;
+    bool aborted = false;
+
+    while (1)
+    {
+        rc = sqlite3_step(pStmt);
+        if (rc == SQLITE_ROW)
+        {
+            VariantVector colValues(numCols);
+            for (unsigned i = 0; i < numCols; ++i)
+            {
+                int type = sqlite3_column_type(pStmt, i);
+                if (type != SQLITE_NULL)
+                {
+                    // We can only bind primitive data type that our Variant class supports
+                    switch (type)
+                    {
+                    case SQLITE_INTEGER:
+                        colValues[i] = sqlite3_column_int(pStmt, i);
+                        if (String(sqlite3_column_decltype(pStmt, i)).Compare("BOOLEAN", false) == 0)
+                            colValues[i] = colValues[i] != 0;
+                        break;
+
+                    case SQLITE_FLOAT:
+                        colValues[i] = sqlite3_column_double(pStmt, i);
+                        break;
+
+                    default:
+                        // All other types are stored using their string representation in the Variant
+                        colValues[i] = (const char*)sqlite3_column_text(pStmt, i);
+                        break;
+                    }
+                }
+            }
+
+            if (useCursorEvent)
+            {
+                using namespace DbCursor;
+
+                VariantMap& eventData = GetEventDataMap();
+                eventData[P_DBCONNECTION] = this;
+                eventData[P_RESULTIMPL] = pStmt;
+                eventData[P_SQL] = sql;
+                eventData[P_NUMCOLS] = numCols;
+                eventData[P_COLVALUES] = colValues;
+                eventData[P_COLHEADERS] = result.columns_;
+                eventData[P_FILTER] = false;
+                eventData[P_ABORT] = false;
+
+                SendEvent(E_DBCURSOR, eventData);
+
+                filtered = eventData[P_FILTER].GetBool();
+                aborted = eventData[P_ABORT].GetBool();
+            }
+
+            if (!filtered)
+                result.rows_.Push(colValues);
+            if (aborted)
+            {
+                sqlite3_finalize(pStmt);
+                break;
+            }
+        }
+        else if (rc != SQLITE_DONE)
+            LOGERRORF("Could not execute: %s", sqlite3_errmsg(connectionImpl_));
+        if (rc != SQLITE_ROW)
+        {
+            sqlite3_finalize(pStmt);
+            break;
+        }
+    }
+
+    result.numAffectedRows_ = numCols ? -1 : sqlite3_changes(connectionImpl_);
+    return result;
+}
+
+}

+ 65 - 0
Source/Atomic/Database/SQLite/SQLiteConnection.h

@@ -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.
+//
+
+#pragma once
+
+#include "../../Core/Object.h"
+#include "../../Database/DbResult.h"
+
+#include <SQLite/sqlite3.h>
+
+namespace Urho3D
+{
+
+/// %Database connection.
+class URHO3D_API DbConnection : public Object
+{
+    OBJECT(DbConnection);
+
+public:
+    /// Construct.
+    DbConnection(Context* context, const String& connectionString);
+    /// Destruct.
+    ~DbConnection();
+    /// Finalize all prepared statements, close all BLOB handles, and finish all sqlite3_backup objects
+    void Finalize();
+
+    /// Execute an SQL statements immediately. Send E_DBCURSOR event for each row in the resultset when useCursorEvent parameter is set to true.
+    DbResult Execute(const String& sql, bool useCursorEvent = false);
+
+    /// Return database connection string. The connection string for SQLite3 is using the URI format described in https://www.sqlite.org/uri.html, while the connection string for ODBC is using DSN format as per ODBC standard.
+    const String& GetConnectionString() const { return connectionString_; }
+
+    /// Return the underlying implementation connection object pointer. It is sqlite* when using SQLite3 or nanodbc::connection* when using ODBC.
+    const sqlite3* GetConnectionImpl() const { return connectionImpl_; }
+
+    /// Return true when the connection object is connected to the associated database.
+    bool IsConnected() const { return connectionImpl_ != 0; }
+
+private:
+    /// The connection string for SQLite3 is using the URI format described in https://www.sqlite.org/uri.html, while the connection string for ODBC is using DSN format as per ODBC standard.
+    String connectionString_;
+    /// The underlying implementation connection object.
+    sqlite3* connectionImpl_;
+};
+
+}

+ 68 - 0
Source/Atomic/Database/SQLite/SQLiteResult.h

@@ -0,0 +1,68 @@
+//
+// 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/Variant.h"
+
+#include <SQLite/sqlite3.h>
+
+namespace Urho3D
+{
+
+/// %Database query result.
+class URHO3D_API DbResult
+{
+    friend class DbConnection;
+
+public:
+    // Default constructor constructs an empty result object.
+    DbResult() :
+        numAffectedRows_(-1)
+    {
+    }
+
+    /// Return number of columns in the resultset or 0 if there is no resultset.
+    unsigned GetNumColumns() const { return columns_.Size(); }
+
+    /// Return number of rows in the resultset or 0 if the number of rows is not available.
+    unsigned GetNumRows() const { return rows_.Size(); }
+
+    /// Return number of affected rows by the DML query or -1 if the number of affected rows is not available.
+    long GetNumAffectedRows() const { return numAffectedRows_; }
+
+    /// Return the column headers string collection.
+    const StringVector& GetColumns() const { return columns_; }
+
+    /// Return fetched rows collection. Filtered rows are not included in the collection.
+    const Vector<VariantVector>& GetRows() const { return rows_; }
+
+private:
+    /// Column headers from the resultset.
+    StringVector columns_;
+    /// Fetched rows from the resultset.
+    Vector<VariantVector> rows_;
+    /// Number of affected rows by recent DML query.
+    long numAffectedRows_;
+};
+
+}

+ 0 - 3
Source/Atomic/Engine/Application.cpp

@@ -111,13 +111,10 @@ void Application::ErrorExit(const String& message)
     engine_->Exit(); // Close the rendering window
     engine_->Exit(); // Close the rendering window
     exitCode_ = EXIT_FAILURE;
     exitCode_ = EXIT_FAILURE;
 
 
-    // Only for WIN32, otherwise the error messages would be double posted on Mac OS X and Linux platforms
     if (!message.Length())
     if (!message.Length())
     {
     {
-#ifdef WIN32
         ErrorDialog(GetTypeName(), startupErrors_.Length() ? startupErrors_ :
         ErrorDialog(GetTypeName(), startupErrors_.Length() ? startupErrors_ :
             "Application has been terminated due to unexpected error.");
             "Application has been terminated due to unexpected error.");
-#endif
     }
     }
     else
     else
         ErrorDialog(GetTypeName(), message);
         ErrorDialog(GetTypeName(), message);

+ 9 - 0
Source/Atomic/Engine/Engine.cpp

@@ -41,11 +41,15 @@
 #ifdef ATOMIC_NETWORK
 #ifdef ATOMIC_NETWORK
 #include "../Network/Network.h"
 #include "../Network/Network.h"
 #endif
 #endif
+#ifdef ATOMIC_DATABASE
+#include "../Database/Database.h"
+#endif
 #ifdef ATOMIC_PHYSICS
 #ifdef ATOMIC_PHYSICS
 #include "../Physics/PhysicsWorld.h"
 #include "../Physics/PhysicsWorld.h"
 #endif
 #endif
 #include "../Resource/XMLFile.h"
 #include "../Resource/XMLFile.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
+#include "../Resource/Localization.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
 #include "../Scene/SceneEvents.h"
 #include "../Scene/SceneEvents.h"
 #include "../UI/UI.h"
 #include "../UI/UI.h"
@@ -56,6 +60,7 @@
 #include "../Atomic3D/Atomic3D.h"
 #include "../Atomic3D/Atomic3D.h"
 #endif
 #endif
 
 
+
 #if defined(EMSCRIPTEN) && defined(ATOMIC_TESTING)
 #if defined(EMSCRIPTEN) && defined(ATOMIC_TESTING)
 #include <emscripten.h>
 #include <emscripten.h>
 #endif
 #endif
@@ -121,8 +126,12 @@ Engine::Engine(Context* context) :
     context_->RegisterSubsystem(new Log(context_));
     context_->RegisterSubsystem(new Log(context_));
 #endif
 #endif
     context_->RegisterSubsystem(new ResourceCache(context_));
     context_->RegisterSubsystem(new ResourceCache(context_));
+    context_->RegisterSubsystem(new Localization(context_));
 #ifdef ATOMIC_NETWORK
 #ifdef ATOMIC_NETWORK
     context_->RegisterSubsystem(new Network(context_));
     context_->RegisterSubsystem(new Network(context_));
+#endif
+#ifdef ATOMIC_DATABASE
+    context_->RegisterSubsystem(new Database(context_));
 #endif
 #endif
     context_->RegisterSubsystem(new Input(context_));
     context_->RegisterSubsystem(new Input(context_));
     context_->RegisterSubsystem(new Audio(context_));
     context_->RegisterSubsystem(new Audio(context_));

+ 17 - 5
Source/Atomic/Graphics/Batch.cpp

@@ -42,7 +42,9 @@ namespace Atomic
 
 
 inline bool CompareBatchesState(Batch* lhs, Batch* rhs)
 inline bool CompareBatchesState(Batch* lhs, Batch* rhs)
 {
 {
-    if (lhs->sortKey_ != rhs->sortKey_)
+    if (lhs->renderOrder_ != rhs->renderOrder_)
+        return lhs->renderOrder_ < rhs->renderOrder_;
+    else if (lhs->sortKey_ != rhs->sortKey_)
         return lhs->sortKey_ < rhs->sortKey_;
         return lhs->sortKey_ < rhs->sortKey_;
     else
     else
         return lhs->distance_ < rhs->distance_;
         return lhs->distance_ < rhs->distance_;
@@ -50,7 +52,9 @@ inline bool CompareBatchesState(Batch* lhs, Batch* rhs)
 
 
 inline bool CompareBatchesFrontToBack(Batch* lhs, Batch* rhs)
 inline bool CompareBatchesFrontToBack(Batch* lhs, Batch* rhs)
 {
 {
-    if (lhs->distance_ != rhs->distance_)
+    if (lhs->renderOrder_ != rhs->renderOrder_)
+        return lhs->renderOrder_ < rhs->renderOrder_;
+    else if (lhs->distance_ != rhs->distance_)
         return lhs->distance_ < rhs->distance_;
         return lhs->distance_ < rhs->distance_;
     else
     else
         return lhs->sortKey_ < rhs->sortKey_;
         return lhs->sortKey_ < rhs->sortKey_;
@@ -58,7 +62,9 @@ inline bool CompareBatchesFrontToBack(Batch* lhs, Batch* rhs)
 
 
 inline bool CompareBatchesBackToFront(Batch* lhs, Batch* rhs)
 inline bool CompareBatchesBackToFront(Batch* lhs, Batch* rhs)
 {
 {
-    if (lhs->distance_ != rhs->distance_)
+    if (lhs->renderOrder_ != rhs->renderOrder_)
+        return lhs->renderOrder_ < rhs->renderOrder_;
+    else if (lhs->distance_ != rhs->distance_)
         return lhs->distance_ > rhs->distance_;
         return lhs->distance_ > rhs->distance_;
     else
     else
         return lhs->sortKey_ < rhs->sortKey_;
         return lhs->sortKey_ < rhs->sortKey_;
@@ -69,6 +75,11 @@ inline bool CompareInstancesFrontToBack(const InstanceData& lhs, const InstanceD
     return lhs.distance_ < rhs.distance_;
     return lhs.distance_ < rhs.distance_;
 }
 }
 
 
+inline bool CompareBatchGroupOrder(BatchGroup* lhs, BatchGroup* rhs)
+{
+    return lhs->renderOrder_ < rhs->renderOrder_;
+}
+
 void CalculateShadowMatrix(Matrix4& dest, LightBatchQueue* queue, unsigned split, Renderer* renderer, const Vector3& translation)
 void CalculateShadowMatrix(Matrix4& dest, LightBatchQueue* queue, unsigned split, Renderer* renderer, const Vector3& translation)
 {
 {
     Camera* shadowCamera = queue->shadowSplits_[split].shadowCamera_;
     Camera* shadowCamera = queue->shadowSplits_[split].shadowCamera_;
@@ -669,7 +680,7 @@ void BatchGroup::Draw(View* view, bool allowDepthWrite) const
 unsigned BatchGroupKey::ToHash() const
 unsigned BatchGroupKey::ToHash() const
 {
 {
     return (unsigned)((size_t)zone_ / sizeof(Zone) + (size_t)lightQueue_ / sizeof(LightBatchQueue) + (size_t)pass_ / sizeof(Pass) +
     return (unsigned)((size_t)zone_ / sizeof(Zone) + (size_t)lightQueue_ / sizeof(LightBatchQueue) + (size_t)pass_ / sizeof(Pass) +
-                      (size_t)material_ / sizeof(Material) + (size_t)geometry_ / sizeof(Geometry));
+                      (size_t)material_ / sizeof(Material) + (size_t)geometry_ / sizeof(Geometry)) + renderOrder_;
 }
 }
 
 
 void BatchQueue::Clear(int maxSortedInstances)
 void BatchQueue::Clear(int maxSortedInstances)
@@ -689,12 +700,13 @@ void BatchQueue::SortBackToFront()
 
 
     Sort(sortedBatches_.Begin(), sortedBatches_.End(), CompareBatchesBackToFront);
     Sort(sortedBatches_.Begin(), sortedBatches_.End(), CompareBatchesBackToFront);
 
 
-    // Do not actually sort batch groups, just list them
     sortedBatchGroups_.Resize(batchGroups_.Size());
     sortedBatchGroups_.Resize(batchGroups_.Size());
 
 
     unsigned index = 0;
     unsigned index = 0;
     for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
     for (HashMap<BatchGroupKey, BatchGroup>::Iterator i = batchGroups_.Begin(); i != batchGroups_.End(); ++i)
         sortedBatchGroups_[index++] = &i->second_;
         sortedBatchGroups_[index++] = &i->second_;
+
+    Sort(sortedBatchGroups_.Begin(), sortedBatchGroups_.End(), CompareBatchGroupOrder);
 }
 }
 
 
 void BatchQueue::SortFrontToBack()
 void BatchQueue::SortFrontToBack()

+ 18 - 12
Source/Atomic/Graphics/Batch.h

@@ -24,6 +24,7 @@
 
 
 #include "../Container/Ptr.h"
 #include "../Container/Ptr.h"
 #include "../Graphics/Drawable.h"
 #include "../Graphics/Drawable.h"
+#include "../Graphics/Material.h"
 #include "../Math/MathDefs.h"
 #include "../Math/MathDefs.h"
 #include "../Math/Matrix3x4.h"
 #include "../Math/Matrix3x4.h"
 #include "../Math/Rect.h"
 #include "../Math/Rect.h"
@@ -50,21 +51,22 @@ struct Batch
 {
 {
     /// Construct with defaults.
     /// Construct with defaults.
     Batch() :
     Batch() :
-        lightQueue_(0),
-        isBase_(false)
+        isBase_(false),
+        lightQueue_(0)
     {
     {
     }
     }
 
 
     /// Construct from a drawable's source batch.
     /// Construct from a drawable's source batch.
     Batch(const SourceBatch& rhs) :
     Batch(const SourceBatch& rhs) :
         distance_(rhs.distance_),
         distance_(rhs.distance_),
+        renderOrder_(rhs.material_ ? rhs.material_->GetRenderOrder() : DEFAULT_RENDER_ORDER),
+        isBase_(false),
         geometry_(rhs.geometry_),
         geometry_(rhs.geometry_),
         material_(rhs.material_),
         material_(rhs.material_),
         worldTransform_(rhs.worldTransform_),
         worldTransform_(rhs.worldTransform_),
         numWorldTransforms_(rhs.numWorldTransforms_),
         numWorldTransforms_(rhs.numWorldTransforms_),
         lightQueue_(0),
         lightQueue_(0),
-        geometryType_(rhs.geometryType_),
-        isBase_(false)
+        geometryType_(rhs.geometryType_)
     {
     {
     }
     }
 
 
@@ -74,11 +76,16 @@ struct Batch
     void Prepare(View* view, bool setModelTransform, bool allowDepthWrite) const;
     void Prepare(View* view, bool setModelTransform, bool allowDepthWrite) const;
     /// Prepare and draw.
     /// Prepare and draw.
     void Draw(View* view, bool allowDepthWrite) const;
     void Draw(View* view, bool allowDepthWrite) const;
-
     /// State sorting key.
     /// State sorting key.
     unsigned long long sortKey_;
     unsigned long long sortKey_;
     /// Distance from camera.
     /// Distance from camera.
     float distance_;
     float distance_;
+    /// 8-bit render order modifier from material.
+    unsigned char renderOrder_;
+    /// 8-bit light mask for stencil marking in deferred rendering.
+    unsigned char lightMask_;
+   /// Base batch flag. This tells to draw the object fully without light optimizations.
+    bool isBase_;
     /// Geometry.
     /// Geometry.
     Geometry* geometry_;
     Geometry* geometry_;
     /// Material.
     /// Material.
@@ -101,10 +108,6 @@ struct Batch
     ShaderVariation* pixelShader_;
     ShaderVariation* pixelShader_;
     /// %Geometry type.
     /// %Geometry type.
     GeometryType geometryType_;
     GeometryType geometryType_;
-    /// Base batch flag. This tells to draw the object fully without light optimizations.
-    bool isBase_;
-    /// 8-bit light mask for stencil marking in deferred rendering.
-    unsigned char lightMask_;
 };
 };
 
 
 /// Data for one geometry instance.
 /// Data for one geometry instance.
@@ -187,7 +190,8 @@ struct BatchGroupKey
         lightQueue_(batch.lightQueue_),
         lightQueue_(batch.lightQueue_),
         pass_(batch.pass_),
         pass_(batch.pass_),
         material_(batch.material_),
         material_(batch.material_),
-        geometry_(batch.geometry_)
+        geometry_(batch.geometry_),
+        renderOrder_(batch.renderOrder_)
     {
     {
     }
     }
 
 
@@ -201,19 +205,21 @@ struct BatchGroupKey
     Material* material_;
     Material* material_;
     /// Geometry.
     /// Geometry.
     Geometry* geometry_;
     Geometry* geometry_;
+    /// 8-bit render order modifier from material.
+    unsigned char renderOrder_;
 
 
     /// Test for equality with another batch group key.
     /// Test for equality with another batch group key.
     bool operator ==(const BatchGroupKey& rhs) const
     bool operator ==(const BatchGroupKey& rhs) const
     {
     {
         return zone_ == rhs.zone_ && lightQueue_ == rhs.lightQueue_ && pass_ == rhs.pass_ && material_ == rhs.material_ &&
         return zone_ == rhs.zone_ && lightQueue_ == rhs.lightQueue_ && pass_ == rhs.pass_ && material_ == rhs.material_ &&
-               geometry_ == rhs.geometry_;
+               geometry_ == rhs.geometry_ && renderOrder_ == rhs.renderOrder_;
     }
     }
 
 
     /// Test for inequality with another batch group key.
     /// Test for inequality with another batch group key.
     bool operator !=(const BatchGroupKey& rhs) const
     bool operator !=(const BatchGroupKey& rhs) const
     {
     {
         return zone_ != rhs.zone_ || lightQueue_ != rhs.lightQueue_ || pass_ != rhs.pass_ || material_ != rhs.material_ ||
         return zone_ != rhs.zone_ || lightQueue_ != rhs.lightQueue_ || pass_ != rhs.pass_ || material_ != rhs.material_ ||
-               geometry_ != rhs.geometry_;
+               geometry_ != rhs.geometry_ || renderOrder_ != rhs.renderOrder_;
     }
     }
 
 
     /// Return hash value.
     /// Return hash value.

+ 66 - 35
Source/Atomic/Graphics/Direct3D11/D3D11Graphics.cpp

@@ -305,6 +305,11 @@ Graphics::~Graphics()
         impl_->defaultDepthTexture_->Release();
         impl_->defaultDepthTexture_->Release();
         impl_->defaultDepthTexture_ = 0;
         impl_->defaultDepthTexture_ = 0;
     }
     }
+    if (impl_->resolveTexture_)
+    {
+        impl_->resolveTexture_->Release();
+        impl_->resolveTexture_ = 0;
+    }
     if (impl_->swapChain_)
     if (impl_->swapChain_)
     {
     {
         impl_->swapChain_->Release();
         impl_->swapChain_->Release();
@@ -619,21 +624,18 @@ bool Graphics::TakeScreenShot(Image* destImage)
     if (multiSample_ > 1)
     if (multiSample_ > 1)
     {
     {
         // If backbuffer is multisampled, need another DEFAULT usage texture to resolve the data to first
         // 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;
+        CreateResolveTexture();
 
 
-        impl_->device_->CreateTexture2D(&textureDesc, 0, &resolveTexture);
-        if (!resolveTexture)
+        if (!impl_->resolveTexture_)
         {
         {
             LOGERROR("Could not create intermediate texture for multisampled screenshot");
             LOGERROR("Could not create intermediate texture for multisampled screenshot");
             stagingTexture->Release();
             stagingTexture->Release();
+            source->Release();
             return false;
             return false;
         }
         }
 
 
-        impl_->deviceContext_->ResolveSubresource(resolveTexture, 0, source, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
-        impl_->deviceContext_->CopyResource(stagingTexture, resolveTexture);
-        resolveTexture->Release();
+        impl_->deviceContext_->ResolveSubresource(impl_->resolveTexture_, 0, source, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
+        impl_->deviceContext_->CopyResource(stagingTexture, impl_->resolveTexture_);
     }
     }
     else
     else
         impl_->deviceContext_->CopyResource(stagingTexture, source);
         impl_->deviceContext_->CopyResource(stagingTexture, source);
@@ -795,41 +797,45 @@ bool Graphics::ResolveToTexture(Texture2D* destination, const IntRect& viewport)
     if (vpCopy.bottom_ <= vpCopy.top_)
     if (vpCopy.bottom_ <= vpCopy.top_)
         vpCopy.bottom_ = vpCopy.top_ + 1;
         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();
+    D3D11_BOX srcBox;
+    srcBox.left = Clamp(vpCopy.left_, 0, width_);
+    srcBox.top = Clamp(vpCopy.top_, 0, height_);
+    srcBox.right = Clamp(vpCopy.right_, 0, width_);
+    srcBox.bottom = Clamp(vpCopy.bottom_, 0, height_);
+    srcBox.front = 0;
+    srcBox.back = 1;
 
 
     ID3D11Resource* source = 0;
     ID3D11Resource* source = 0;
-    bool resolve = false;
-    bool needRelease = false;
+    bool resolve = multiSample_ > 1;
+    impl_->defaultRenderTargetView_->GetResource(&source);
 
 
-    if (renderTargets_[0])
-        source = (ID3D11Resource*)renderTargets_[0]->GetParentTexture()->GetGPUObject();
-    else
+    if (!resolve)
     {
     {
-        impl_->defaultRenderTargetView_->GetResource(&source);
-        resolve = multiSample_ > 1;
-        needRelease = true;
+        if (!srcBox.left && !srcBox.top && srcBox.right == width_ && srcBox.bottom == height_)
+            impl_->deviceContext_->CopyResource((ID3D11Resource*)destination->GetGPUObject(), source);
+        else
+            impl_->deviceContext_->CopySubresourceRegion((ID3D11Resource*)destination->GetGPUObject(), 0, 0, 0, 0, source, 0, &srcBox);
     }
     }
-
-    if (!resolve)
-        impl_->deviceContext_->CopyResource((ID3D11Resource*)destination->GetGPUObject(), source);
     else
     else
     {
     {
-        impl_->deviceContext_->ResolveSubresource((ID3D11Resource*)destination->GetGPUObject(), 0, source, 0, (DXGI_FORMAT)
-            destination->GetFormat());
+        if (!srcBox.left && !srcBox.top && srcBox.right == width_ && srcBox.bottom == height_)
+        {
+            impl_->deviceContext_->ResolveSubresource((ID3D11Resource*)destination->GetGPUObject(), 0, source, 0, (DXGI_FORMAT)
+                destination->GetFormat());
+        }
+        else
+        {
+            CreateResolveTexture();
+
+            if (impl_->resolveTexture_)
+            {
+                impl_->deviceContext_->ResolveSubresource(impl_->resolveTexture_, 0, source, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
+                impl_->deviceContext_->CopySubresourceRegion((ID3D11Resource*)destination->GetGPUObject(), 0, 0, 0, 0, impl_->resolveTexture_, 0, &srcBox);
+            }
+        }
     }
     }
 
 
-    if (needRelease)
-        source->Release();
+    source->Release();
 
 
     return true;
     return true;
 }
 }
@@ -1296,7 +1302,7 @@ bool Graphics::HasShaderParameter(StringHash param)
 
 
 bool Graphics::HasTextureUnit(TextureUnit unit)
 bool Graphics::HasTextureUnit(TextureUnit unit)
 {
 {
-    return pixelShader_ && pixelShader_->HasTextureUnit(unit);
+    return (vertexShader_ && vertexShader_->HasTextureUnit(unit)) || (pixelShader_ && pixelShader_->HasTextureUnit(unit));
 }
 }
 
 
 void Graphics::ClearParameterSource(ShaderParameterGroup group)
 void Graphics::ClearParameterSource(ShaderParameterGroup group)
@@ -2353,6 +2359,11 @@ bool Graphics::UpdateSwapChain(int width, int height)
         impl_->defaultDepthTexture_->Release();
         impl_->defaultDepthTexture_->Release();
         impl_->defaultDepthTexture_ = 0;
         impl_->defaultDepthTexture_ = 0;
     }
     }
+    if (impl_->resolveTexture_)
+    {
+        impl_->resolveTexture_->Release();
+        impl_->resolveTexture_ = 0;
+    }
 
 
     impl_->depthStencilView_ = 0;
     impl_->depthStencilView_ = 0;
     for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
     for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
@@ -2714,6 +2725,26 @@ void Graphics::PrepareDraw()
     dirtyConstantBuffers_.Clear();
     dirtyConstantBuffers_.Clear();
 }
 }
 
 
+void Graphics::CreateResolveTexture()
+{
+    if (impl_->resolveTexture_)
+        return;
+
+    D3D11_TEXTURE2D_DESC textureDesc;
+    memset(&textureDesc, 0, sizeof textureDesc);
+    textureDesc.Width = (UINT)width_;
+    textureDesc.Height = (UINT)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_DEFAULT;
+    textureDesc.CPUAccessFlags = 0;
+
+    impl_->device_->CreateTexture2D(&textureDesc, 0, &impl_->resolveTexture_);
+}
+
 void Graphics::SetTextureUnitMappings()
 void Graphics::SetTextureUnitMappings()
 {
 {
     textureUnits_["DiffMap"] = TU_DIFFUSE;
     textureUnits_["DiffMap"] = TU_DIFFUSE;

+ 3 - 1
Source/Atomic/Graphics/Direct3D11/D3D11Graphics.h

@@ -175,7 +175,7 @@ public:
     bool NeedParameterUpdate(ShaderParameterGroup group, const void* source);
     bool NeedParameterUpdate(ShaderParameterGroup group, const void* source);
     /// Check whether a shader parameter exists on the currently set shaders.
     /// Check whether a shader parameter exists on the currently set shaders.
     bool HasShaderParameter(StringHash param);
     bool HasShaderParameter(StringHash param);
-    /// Check whether the current pixel shader uses a texture unit.
+    /// Check whether the current vertex or pixel shader uses a texture unit.
     bool HasTextureUnit(TextureUnit unit);
     bool HasTextureUnit(TextureUnit unit);
     /// Clear remembered shader parameter source group.
     /// Clear remembered shader parameter source group.
     void ClearParameterSource(ShaderParameterGroup group);
     void ClearParameterSource(ShaderParameterGroup group);
@@ -518,6 +518,8 @@ private:
     void SetTextureUnitMappings();
     void SetTextureUnitMappings();
     /// Process dirtied state before draw.
     /// Process dirtied state before draw.
     void PrepareDraw();
     void PrepareDraw();
+    /// Create intermediate texture for multisampled backbuffer resolve. No-op if already exists.
+    void CreateResolveTexture();
 
 
     /// Mutex for accessing the GPU objects vector from several threads.
     /// Mutex for accessing the GPU objects vector from several threads.
     Mutex gpuObjectMutex_;
     Mutex gpuObjectMutex_;

+ 2 - 1
Source/Atomic/Graphics/Direct3D11/D3D11GraphicsImpl.cpp

@@ -38,7 +38,8 @@ GraphicsImpl::GraphicsImpl() :
     defaultRenderTargetView_(0),
     defaultRenderTargetView_(0),
     defaultDepthTexture_(0),
     defaultDepthTexture_(0),
     defaultDepthStencilView_(0),
     defaultDepthStencilView_(0),
-    depthStencilView_(0)
+    depthStencilView_(0),
+    resolveTexture_(0)
 {
 {
     for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
     for (unsigned i = 0; i < MAX_RENDERTARGETS; ++i)
         renderTargetViews_[i] = 0;
         renderTargetViews_[i] = 0;

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

@@ -78,6 +78,8 @@ private:
     HashMap<unsigned, ID3D11DepthStencilState*> depthStates_;
     HashMap<unsigned, ID3D11DepthStencilState*> depthStates_;
     /// Created rasterizer state objects.
     /// Created rasterizer state objects.
     HashMap<unsigned, ID3D11RasterizerState*> rasterizerStates_;
     HashMap<unsigned, ID3D11RasterizerState*> rasterizerStates_;
+    /// Intermediate texture for multisampled screenshots and less than whole viewport multisampled resolve, created on demand.
+    ID3D11Texture2D* resolveTexture_;
     /// Bound shader resource views.
     /// Bound shader resource views.
     ID3D11ShaderResourceView* shaderResourceViews_[MAX_TEXTURE_UNITS];
     ID3D11ShaderResourceView* shaderResourceViews_[MAX_TEXTURE_UNITS];
     /// Bound sampler state objects.
     /// Bound sampler state objects.

+ 1 - 1
Source/Atomic/Graphics/Direct3D11/D3D11ShaderVariation.cpp

@@ -366,7 +366,7 @@ void ShaderVariation::ParseParameters(unsigned char* bufData, unsigned bufSize)
         String resourceName(resourceDesc.Name);
         String resourceName(resourceDesc.Name);
         if (resourceDesc.Type == D3D_SIT_CBUFFER)
         if (resourceDesc.Type == D3D_SIT_CBUFFER)
             cbRegisterMap[resourceName] = resourceDesc.BindPoint;
             cbRegisterMap[resourceName] = resourceDesc.BindPoint;
-        else if (type_ == PS && resourceDesc.Type == D3D_SIT_SAMPLER && resourceDesc.BindPoint < MAX_TEXTURE_UNITS)
+        else if (resourceDesc.Type == D3D_SIT_SAMPLER && resourceDesc.BindPoint < MAX_TEXTURE_UNITS)
             useTextureUnit_[resourceDesc.BindPoint] = true;
             useTextureUnit_[resourceDesc.BindPoint] = true;
     }
     }
 
 

+ 68 - 54
Source/Atomic/Graphics/Direct3D11/D3D11TextureCube.cpp

@@ -113,8 +113,6 @@ bool TextureCube::BeginLoad(Deserializer& source)
         if (GetPath(name).Empty())
         if (GetPath(name).Empty())
             name = texPath + name;
             name = texPath + name;
 
 
-        CubeMapLayout layout =
-            (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
         SharedPtr<Image> image = cache->GetTempResource<Image>(name);
         SharedPtr<Image> image = cache->GetTempResource<Image>(name);
         if (!image)
         if (!image)
             return false;
             return false;
@@ -122,63 +120,79 @@ bool TextureCube::BeginLoad(Deserializer& source)
         int faceWidth, faceHeight;
         int faceWidth, faceHeight;
         loadImages_.Resize(MAX_CUBEMAP_FACES);
         loadImages_.Resize(MAX_CUBEMAP_FACES);
 
 
-        switch (layout)
+        if (image->IsCubemap())
+        {
+            loadImages_[FACE_POSITIVE_X] = image;
+            loadImages_[FACE_NEGATIVE_X] = loadImages_[FACE_POSITIVE_X]->GetNextSibling();
+            loadImages_[FACE_POSITIVE_Y] = loadImages_[FACE_NEGATIVE_X]->GetNextSibling();
+            loadImages_[FACE_NEGATIVE_Y] = loadImages_[FACE_POSITIVE_Y]->GetNextSibling();
+            loadImages_[FACE_POSITIVE_Z] = loadImages_[FACE_NEGATIVE_Y]->GetNextSibling();
+            loadImages_[FACE_NEGATIVE_Z] = loadImages_[FACE_POSITIVE_Z]->GetNextSibling();
+        }
+        else
         {
         {
-        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;
+            CubeMapLayout layout =
+                (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
 
 
-        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])
+            switch (layout)
             {
             {
-                loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
-                loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
+            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;
             }
             }
-            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
     // Face per image

+ 67 - 56
Source/Atomic/Graphics/Direct3D9/D3D9TextureCube.cpp

@@ -121,8 +121,6 @@ bool TextureCube::BeginLoad(Deserializer& source)
         if (GetPath(name).Empty())
         if (GetPath(name).Empty())
             name = texPath + name;
             name = texPath + name;
 
 
-        CubeMapLayout layout =
-            (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
         SharedPtr<Image> image = cache->GetTempResource<Image>(name);
         SharedPtr<Image> image = cache->GetTempResource<Image>(name);
         if (!image)
         if (!image)
             return false;
             return false;
@@ -130,63 +128,76 @@ bool TextureCube::BeginLoad(Deserializer& source)
         int faceWidth, faceHeight;
         int faceWidth, faceHeight;
         loadImages_.Resize(MAX_CUBEMAP_FACES);
         loadImages_.Resize(MAX_CUBEMAP_FACES);
 
 
-        switch (layout)
+        if (image->IsCubemap())
         {
         {
-        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_POSITIVE_X] = image;
+            loadImages_[FACE_NEGATIVE_X] = loadImages_[FACE_POSITIVE_X]->GetNextSibling();
+            loadImages_[FACE_POSITIVE_Y] = loadImages_[FACE_NEGATIVE_X]->GetNextSibling();
+            loadImages_[FACE_NEGATIVE_Y] = loadImages_[FACE_POSITIVE_Y]->GetNextSibling();
+            loadImages_[FACE_POSITIVE_Z] = loadImages_[FACE_NEGATIVE_Y]->GetNextSibling();
+            loadImages_[FACE_NEGATIVE_Z] = loadImages_[FACE_POSITIVE_Z]->GetNextSibling();
+        }
+        else
+        {
+            CubeMapLayout layout = (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
+            switch (layout)
             {
             {
-                loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
-                loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
+            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;
             }
             }
-            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
     // Face per image

+ 4 - 0
Source/Atomic/Graphics/Drawable.cpp

@@ -35,6 +35,10 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
+#ifdef _MSC_VER
+#pragma warning(disable:6293)
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 15 - 0
Source/Atomic/Graphics/Material.cpp

@@ -390,6 +390,10 @@ bool Material::Load(const XMLElement& source)
     if (depthBiasElem)
     if (depthBiasElem)
         SetDepthBias(BiasParameters(depthBiasElem.GetFloat("constant"), depthBiasElem.GetFloat("slopescaled")));
         SetDepthBias(BiasParameters(depthBiasElem.GetFloat("constant"), depthBiasElem.GetFloat("slopescaled")));
 
 
+    XMLElement renderOrderElem = source.GetChild("renderorder");
+    if (renderOrderElem)
+        SetRenderOrder((unsigned char)renderOrderElem.GetUInt("value"));
+
     RefreshShaderParameterHash();
     RefreshShaderParameterHash();
     RefreshMemoryUse();
     RefreshMemoryUse();
     CheckOcclusion();
     CheckOcclusion();
@@ -468,6 +472,10 @@ bool Material::Save(XMLElement& dest) const
     depthBiasElem.SetFloat("constant", depthBias_.constantBias_);
     depthBiasElem.SetFloat("constant", depthBias_.constantBias_);
     depthBiasElem.SetFloat("slopescaled", depthBias_.slopeScaledBias_);
     depthBiasElem.SetFloat("slopescaled", depthBias_.slopeScaledBias_);
 
 
+    // Write render order
+    XMLElement renderOrderElem = dest.CreateChild("renderorder");
+    renderOrderElem.SetUInt("value", renderOrder_);
+
     return true;
     return true;
 }
 }
 
 
@@ -632,6 +640,11 @@ void Material::SetDepthBias(const BiasParameters& parameters)
     depthBias_.Validate();
     depthBias_.Validate();
 }
 }
 
 
+void Material::SetRenderOrder(unsigned char order)
+{
+    renderOrder_ = order;
+}
+
 void Material::SetScene(Scene* scene)
 void Material::SetScene(Scene* scene)
 {
 {
     UnsubscribeFromEvent(E_UPDATE);
     UnsubscribeFromEvent(E_UPDATE);
@@ -676,6 +689,7 @@ SharedPtr<Material> Material::Clone(const String& cloneName) const
     ret->cullMode_ = cullMode_;
     ret->cullMode_ = cullMode_;
     ret->shadowCullMode_ = shadowCullMode_;
     ret->shadowCullMode_ = shadowCullMode_;
     ret->fillMode_ = fillMode_;
     ret->fillMode_ = fillMode_;
+    ret->renderOrder_ = renderOrder_;
     ret->RefreshMemoryUse();
     ret->RefreshMemoryUse();
 
 
     return ret;
     return ret;
@@ -797,6 +811,7 @@ void Material::ResetToDefaults()
     shadowCullMode_ = CULL_CCW;
     shadowCullMode_ = CULL_CCW;
     fillMode_ = FILL_SOLID;
     fillMode_ = FILL_SOLID;
     depthBias_ = BiasParameters(0.0f, 0.0f);
     depthBias_ = BiasParameters(0.0f, 0.0f);
+    renderOrder_ = DEFAULT_RENDER_ORDER;
 
 
     RefreshShaderParameterHash();
     RefreshShaderParameterHash();
     RefreshMemoryUse();
     RefreshMemoryUse();

+ 9 - 0
Source/Atomic/Graphics/Material.h

@@ -40,6 +40,8 @@ class Texture2D;
 class TextureCube;
 class TextureCube;
 class ValueAnimationInfo;
 class ValueAnimationInfo;
 
 
+static const unsigned char DEFAULT_RENDER_ORDER = 128;
+
 /// %Material's shader parameter definition.
 /// %Material's shader parameter definition.
 struct MaterialShaderParameter
 struct MaterialShaderParameter
 {
 {
@@ -150,6 +152,8 @@ public:
     void SetFillMode(FillMode mode);
     void SetFillMode(FillMode mode);
     /// Set depth bias.
     /// Set depth bias.
     void SetDepthBias(const BiasParameters& parameters);
     void SetDepthBias(const BiasParameters& parameters);
+    /// Set 8-bit render order within pass. Default 128. Lower values will render earlier and higher values later, taking precedence over e.g. state and distance sorting.
+    void SetRenderOrder(unsigned char order);
     /// Associate the material with a scene to ensure that shader parameter animation happens in sync with scene update, respecting the scene time scale. If no scene is set, the global update events will be used.
     /// Associate the material with a scene to ensure that shader parameter animation happens in sync with scene update, respecting the scene time scale. If no scene is set, the global update events will be used.
     void SetScene(Scene* scene);
     void SetScene(Scene* scene);
     /// Remove shader parameter.
     /// Remove shader parameter.
@@ -205,6 +209,9 @@ public:
     /// Return depth bias.
     /// Return depth bias.
     const BiasParameters& GetDepthBias() const { return depthBias_; }
     const BiasParameters& GetDepthBias() const { return depthBias_; }
 
 
+    /// Return render order.
+    unsigned char GetRenderOrder() const { return renderOrder_; }
+
     /// Return last auxiliary view rendered frame number.
     /// Return last auxiliary view rendered frame number.
     unsigned GetAuxViewFrameNumber() const { return auxViewFrameNumber_; }
     unsigned GetAuxViewFrameNumber() const { return auxViewFrameNumber_; }
 
 
@@ -257,6 +264,8 @@ private:
     FillMode fillMode_;
     FillMode fillMode_;
     /// Depth bias parameters.
     /// Depth bias parameters.
     BiasParameters depthBias_;
     BiasParameters depthBias_;
+    /// Render order value.
+    unsigned char renderOrder_;
     /// Last auxiliary view rendered frame number.
     /// Last auxiliary view rendered frame number.
     unsigned auxViewFrameNumber_;
     unsigned auxViewFrameNumber_;
     /// Shader parameter hash value.
     /// Shader parameter hash value.

+ 2 - 1
Source/Atomic/Graphics/OcclusionBuffer.cpp

@@ -147,9 +147,10 @@ void OcclusionBuffer::Clear()
 
 
     int* dest = buffer_;
     int* dest = buffer_;
     int count = width_ * height_;
     int count = width_ * height_;
+    int fillValue = (int)OCCLUSION_Z_SCALE;
 
 
     while (count--)
     while (count--)
-        *dest++ = 0x7fffffff;
+        *dest++ = fillValue;
 
 
     depthHierarchyDirty_ = true;
     depthHierarchyDirty_ = true;
 }
 }

+ 3 - 2
Source/Atomic/Graphics/Octree.cpp

@@ -25,6 +25,7 @@
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Core/CoreEvents.h"
 #include "../Core/CoreEvents.h"
 #include "../Core/Profiler.h"
 #include "../Core/Profiler.h"
+#include "../Core/Thread.h"
 #include "../Core/WorkQueue.h"
 #include "../Core/WorkQueue.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/Graphics.h"
 #include "../Graphics/Graphics.h"
@@ -514,8 +515,8 @@ void Octree::Raycast(RayOctreeQuery& query) const
 
 
     WorkQueue* queue = GetSubsystem<WorkQueue>();
     WorkQueue* queue = GetSubsystem<WorkQueue>();
 
 
-    // If no worker threads or no triangle-level testing, do not create work items
-    if (!queue->GetNumThreads() || query.level_ < RAY_TRIANGLE)
+    // If no worker threads or no triangle-level testing, or we are being called from a worker thread do not create work items
+    if (query.level_ < RAY_TRIANGLE || !queue->GetNumThreads() || !Thread::IsMainThread() || queue->IsCompleting())
         GetDrawablesInternal(query);
         GetDrawablesInternal(query);
     else
     else
     {
     {

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

@@ -363,7 +363,7 @@ bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless,
         return true;
         return true;
     }
     }
 
 
-    // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size. 
+    // 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 zero in fullscreen, use desktop mode
     if (!width || !height)
     if (!width || !height)
     {
     {
@@ -2036,16 +2036,16 @@ unsigned Graphics::GetFormat(CompressedFormat format) const
 #ifdef GL_ES_VERSION_2_0
 #ifdef GL_ES_VERSION_2_0
     case CF_ETC1:
     case CF_ETC1:
         return etcTextureSupport_ ? GL_ETC1_RGB8_OES : 0;
         return etcTextureSupport_ ? GL_ETC1_RGB8_OES : 0;
-        
+
     case CF_PVRTC_RGB_2BPP:
     case CF_PVRTC_RGB_2BPP:
         return pvrtcTextureSupport_ ? COMPRESSED_RGB_PVRTC_2BPPV1_IMG : 0;
         return pvrtcTextureSupport_ ? COMPRESSED_RGB_PVRTC_2BPPV1_IMG : 0;
-        
+
     case CF_PVRTC_RGB_4BPP:
     case CF_PVRTC_RGB_4BPP:
         return pvrtcTextureSupport_ ? COMPRESSED_RGB_PVRTC_4BPPV1_IMG : 0;
         return pvrtcTextureSupport_ ? COMPRESSED_RGB_PVRTC_4BPPV1_IMG : 0;
-        
+
     case CF_PVRTC_RGBA_2BPP:
     case CF_PVRTC_RGBA_2BPP:
         return pvrtcTextureSupport_ ? COMPRESSED_RGBA_PVRTC_2BPPV1_IMG : 0;
         return pvrtcTextureSupport_ ? COMPRESSED_RGBA_PVRTC_2BPPV1_IMG : 0;
-        
+
     case CF_PVRTC_RGBA_4BPP:
     case CF_PVRTC_RGBA_4BPP:
         return pvrtcTextureSupport_ ? COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0;
         return pvrtcTextureSupport_ ? COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0;
 #endif
 #endif

+ 1 - 1
Source/Atomic/Graphics/OpenGL/OGLGraphics.h

@@ -172,7 +172,7 @@ public:
     bool NeedParameterUpdate(ShaderParameterGroup group, const void* source);
     bool NeedParameterUpdate(ShaderParameterGroup group, const void* source);
     /// Check whether a shader parameter exists on the currently set shaders.
     /// Check whether a shader parameter exists on the currently set shaders.
     bool HasShaderParameter(StringHash param);
     bool HasShaderParameter(StringHash param);
-    /// Check whether the current pixel shader uses a texture unit.
+    /// Check whether the current shader program uses a texture unit.
     bool HasTextureUnit(TextureUnit unit);
     bool HasTextureUnit(TextureUnit unit);
     /// Clear remembered shader parameter source group.
     /// Clear remembered shader parameter source group.
     void ClearParameterSource(ShaderParameterGroup group);
     void ClearParameterSource(ShaderParameterGroup group);

+ 68 - 54
Source/Atomic/Graphics/OpenGL/OGLTextureCube.cpp

@@ -122,8 +122,6 @@ bool TextureCube::BeginLoad(Deserializer& source)
         if (GetPath(name).Empty())
         if (GetPath(name).Empty())
             name = texPath + name;
             name = texPath + name;
 
 
-        CubeMapLayout layout =
-            (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
         SharedPtr<Image> image = cache->GetTempResource<Image>(name);
         SharedPtr<Image> image = cache->GetTempResource<Image>(name);
         if (!image)
         if (!image)
             return false;
             return false;
@@ -131,63 +129,79 @@ bool TextureCube::BeginLoad(Deserializer& source)
         int faceWidth, faceHeight;
         int faceWidth, faceHeight;
         loadImages_.Resize(MAX_CUBEMAP_FACES);
         loadImages_.Resize(MAX_CUBEMAP_FACES);
 
 
-        switch (layout)
+        if (image->IsCubemap())
+        {
+            loadImages_[FACE_POSITIVE_X] = image;
+            loadImages_[FACE_NEGATIVE_X] = loadImages_[FACE_POSITIVE_X]->GetNextSibling();
+            loadImages_[FACE_POSITIVE_Y] = loadImages_[FACE_NEGATIVE_X]->GetNextSibling();
+            loadImages_[FACE_NEGATIVE_Y] = loadImages_[FACE_POSITIVE_Y]->GetNextSibling();
+            loadImages_[FACE_POSITIVE_Z] = loadImages_[FACE_NEGATIVE_Y]->GetNextSibling();
+            loadImages_[FACE_NEGATIVE_Z] = loadImages_[FACE_POSITIVE_Z]->GetNextSibling();
+        }
+        else
         {
         {
-        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;
+            CubeMapLayout layout =
+                (CubeMapLayout)GetStringListIndex(imageElem.GetAttribute("layout").CString(), cubeMapLayoutNames, CML_HORIZONTAL);
 
 
-        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])
+            switch (layout)
             {
             {
-                loadImages_[FACE_NEGATIVE_Z]->FlipVertical();
-                loadImages_[FACE_NEGATIVE_Z]->FlipHorizontal();
+            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;
             }
             }
-            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
     // Face per image

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

@@ -30,6 +30,10 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
+#ifdef _MSC_VER
+#pragma warning(disable:6293)
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 4 - 0
Source/Atomic/Graphics/Renderer.cpp

@@ -50,6 +50,10 @@
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
+#ifdef _MSC_VER
+#pragma warning(disable:6293)
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 1 - 1
Source/Atomic/Graphics/Renderer.h

@@ -194,7 +194,7 @@ public:
     void SetMinInstances(int instances);
     void SetMinInstances(int instances);
     /// Set maximum number of sorted instances per batch group. If exceeded, instances are rendered unsorted.
     /// Set maximum number of sorted instances per batch group. If exceeded, instances are rendered unsorted.
     void SetMaxSortedInstances(int instances);
     void SetMaxSortedInstances(int instances);
-    /// Set maximum number of occluder trianges.
+    /// Set maximum number of occluder triangles.
     void SetMaxOccluderTriangles(int triangles);
     void SetMaxOccluderTriangles(int triangles);
     /// Set occluder buffer width.
     /// Set occluder buffer width.
     void SetOcclusionBufferSize(int size);
     void SetOcclusionBufferSize(int size);

+ 1 - 1
Source/Atomic/Graphics/Shader.cpp

@@ -98,7 +98,7 @@ bool Shader::BeginLoad(Deserializer& source)
     CommentOutFunction(vsSourceCode_, "void PS(");
     CommentOutFunction(vsSourceCode_, "void PS(");
     CommentOutFunction(psSourceCode_, "void VS(");
     CommentOutFunction(psSourceCode_, "void VS(");
 
 
-    // OpenGL: rename either VS() or PS() to main(), comment out vertex attributes in pixel shaders
+    // OpenGL: rename either VS() or PS() to main()
 #ifdef ATOMIC_OPENGL
 #ifdef ATOMIC_OPENGL
     vsSourceCode_.Replace("void VS(", "void main(");
     vsSourceCode_.Replace("void VS(", "void main(");
     psSourceCode_.Replace("void PS(", "void main(");
     psSourceCode_.Replace("void PS(", "void main(");

+ 37 - 61
Source/Atomic/Graphics/View.cpp

@@ -295,6 +295,7 @@ View::View(Context* context) :
     camera_(0),
     camera_(0),
     cameraZone_(0),
     cameraZone_(0),
     farClipZone_(0),
     farClipZone_(0),
+    occlusionBuffer_(0),
     renderTarget_(0),
     renderTarget_(0),
     substituteRenderTarget_(0)
     substituteRenderTarget_(0)
 {
 {
@@ -644,7 +645,6 @@ void View::Render()
     octree_ = 0;
     octree_ = 0;
     cameraZone_ = 0;
     cameraZone_ = 0;
     farClipZone_ = 0;
     farClipZone_ = 0;
-    occlusionBuffer_ = 0;
     frame_.camera_ = 0;
     frame_.camera_ = 0;
 }
 }
 
 
@@ -1688,7 +1688,7 @@ void View::SetRenderTargets(RenderPathCommand& command)
         }
         }
     }
     }
 
 
-    // When rendering to the final destination rendertarget, use the actual viewport. Otherwise texture rendertargets should use 
+    // When rendering to the final destination rendertarget, use the actual viewport. Otherwise texture rendertargets should use
     // their full size as the viewport
     // their full size as the viewport
     IntVector2 rtSizeNow = graphics_->GetRenderTargetDimensions();
     IntVector2 rtSizeNow = graphics_->GetRenderTargetDimensions();
     IntRect viewport = (useViewportOutput && currentRenderTarget_ == renderTarget_) ? viewRect_ : IntRect(0, 0, rtSizeNow.x_,
     IntRect viewport = (useViewportOutput && currentRenderTarget_ == renderTarget_) ? viewRect_ : IntRect(0, 0, rtSizeNow.x_,
@@ -1854,33 +1854,50 @@ bool View::CheckPingpong(unsigned index)
 
 
 void View::AllocateScreenBuffers()
 void View::AllocateScreenBuffers()
 {
 {
+    bool hasScenePassToRTs = false;
+    bool hasCustomDepth = false;
+    bool hasViewportRead = false;
+    bool hasPingpong = false;
     bool needSubstitute = false;
     bool needSubstitute = false;
     unsigned numViewportTextures = 0;
     unsigned numViewportTextures = 0;
     depthOnlyDummyTexture_ = 0;
     depthOnlyDummyTexture_ = 0;
 
 
-#ifdef ATOMIC_OPENGL
-    // Due to FBO limitations, in OpenGL deferred modes need to render to texture first and then blit to the backbuffer
-    // Also, if rendering to a texture with full deferred rendering, it must be RGBA to comply with the rest of the buffers,
-    // unless using OpenGL 3
-    if ((deferred_ && !renderTarget_) || (!Graphics::GetGL3Support() && deferredAmbient_ && renderTarget_ &&
-                                          renderTarget_->GetParentTexture()->GetFormat() != Graphics::GetRGBAFormat()))
-        needSubstitute = true;
-    // Also need substitute if rendering to backbuffer using a custom (readable) depth buffer
-    if (!renderTarget_ && !needSubstitute)
+    // Check for commands with special meaning: has custom depth, renders a scene pass to other than the destination viewport,
+    // read the viewport, or pingpong between viewport textures. These may trigger the need to substitute the destination RT
+    for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
     {
     {
-        for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
+        const RenderPathCommand& command = renderPath_->commands_[i];
+        if (!IsNecessary(command))
+            continue;
+        if (!hasViewportRead && CheckViewportRead(command))
+            hasViewportRead = true;
+        if (!hasPingpong && CheckPingpong(i))
+            hasPingpong = true;
+        if (command.depthStencilName_.Length())
+            hasCustomDepth = true;
+        if (!hasScenePassToRTs && command.type_ == CMD_SCENEPASS)
         {
         {
-            const RenderPathCommand& command = renderPath_->commands_[i];
-            if (!IsNecessary(command))
-                continue;
-            if (command.depthStencilName_.Length() && command.outputs_.Size() && !command.outputs_[0].first_.Compare("viewport",
-                false))
+            for (unsigned j = 0; j < command.outputs_.Size(); ++j)
             {
             {
-                needSubstitute = true;
-                break;
+                if (command.outputs_[j].first_.Compare("viewport", false))
+                {
+                    hasScenePassToRTs = true;
+                    break;
+                }
             }
             }
         }
         }
     }
     }
+
+#ifdef ATOMIC_OPENGL
+    // Due to FBO limitations, in OpenGL deferred modes need to render to texture first and then blit to the backbuffer
+    // Also, if rendering to a texture with full deferred rendering, it must be RGBA to comply with the rest of the buffers,
+    // unless using OpenGL 3
+    if (((deferred_ || hasScenePassToRTs) && !renderTarget_) || (!Graphics::GetGL3Support() && deferredAmbient_ && renderTarget_
+        && renderTarget_->GetParentTexture()->GetFormat() != Graphics::GetRGBAFormat()))
+            needSubstitute = true;
+    // Also need substitute if rendering to backbuffer using a custom (readable) depth buffer
+    if (!renderTarget_ && hasCustomDepth)
+        needSubstitute = true;
 #endif
 #endif
     // If backbuffer is antialiased when using deferred rendering, need to reserve a buffer
     // If backbuffer is antialiased when using deferred rendering, need to reserve a buffer
     if (deferred_ && !renderTarget_ && graphics_->GetMultiSample() > 1)
     if (deferred_ && !renderTarget_ && graphics_->GetMultiSample() > 1)
@@ -1889,34 +1906,8 @@ void View::AllocateScreenBuffers()
     // textures will be sized equal to the viewport
     // textures will be sized equal to the viewport
     if (viewSize_.x_ < rtSize_.x_ || viewSize_.y_ < rtSize_.y_)
     if (viewSize_.x_ < rtSize_.x_ || viewSize_.y_ < rtSize_.y_)
     {
     {
-        if (deferred_)
+        if (deferred_ || hasScenePassToRTs || hasCustomDepth)
             needSubstitute = true;
             needSubstitute = true;
-        else if (!needSubstitute)
-        {
-            // Check also if using MRT without deferred rendering and rendering to the viewport and another texture,
-            // or using custom depth
-            for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
-            {
-                const RenderPathCommand& command = renderPath_->commands_[i];
-                if (!IsNecessary(command))
-                    continue;
-                if (command.depthStencilName_.Length())
-                    needSubstitute = true;
-                if (!needSubstitute && command.outputs_.Size() > 1)
-                {
-                    for (unsigned j = 0; j < command.outputs_.Size(); ++j)
-                    {
-                        if (!command.outputs_[j].first_.Compare("viewport", false))
-                        {
-                            needSubstitute = true;
-                            break;
-                        }
-                    }
-                }
-                if (needSubstitute)
-                    break;
-            }
-        }
     }
     }
 
 
     // Follow final rendertarget format, or use RGB to match the backbuffer format
     // Follow final rendertarget format, or use RGB to match the backbuffer format
@@ -1935,21 +1926,6 @@ void View::AllocateScreenBuffers()
         format = Graphics::GetRGBAFormat();
         format = Graphics::GetRGBAFormat();
 #endif
 #endif
 
 
-    // Check for commands which read the viewport, or pingpong between viewport textures
-    bool hasViewportRead = false;
-    bool hasPingpong = false;
-
-    for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
-    {
-        const RenderPathCommand& command = renderPath_->commands_[i];
-        if (!IsNecessary(command))
-            continue;
-        if (CheckViewportRead(command))
-            hasViewportRead = true;
-        if (!hasPingpong && CheckPingpong(i))
-            hasPingpong = true;
-    }
-
     if (hasViewportRead)
     if (hasViewportRead)
     {
     {
         ++numViewportTextures;
         ++numViewportTextures;

+ 4 - 3
Source/Atomic/Graphics/View.h

@@ -85,8 +85,6 @@ struct ScenePassInfo
     bool allowInstancing_;
     bool allowInstancing_;
     /// Mark to stencil flag.
     /// Mark to stencil flag.
     bool markToStencil_;
     bool markToStencil_;
-    /// Light scissor optimization flag.
-    bool useScissor_;
     /// Vertex light flag.
     /// Vertex light flag.
     bool vertexLights_;
     bool vertexLights_;
     /// Batch queue.
     /// Batch queue.
@@ -164,6 +162,9 @@ public:
     /// Return light batch queues.
     /// Return light batch queues.
     const Vector<LightBatchQueue>& GetLightQueues() const { return lightQueues_; }
     const Vector<LightBatchQueue>& GetLightQueues() const { return lightQueues_; }
 
 
+    /// Return the last used software occlusion buffer.
+    OcclusionBuffer* GetOcclusionBuffer() const { return occlusionBuffer_; }
+
     /// Set global (per-frame) shader parameters. Called by Batch and internally by View.
     /// Set global (per-frame) shader parameters. Called by Batch and internally by View.
     void SetGlobalShaderParameters();
     void SetGlobalShaderParameters();
     /// Set camera-specific shader parameters. Called by Batch and internally by View.
     /// Set camera-specific shader parameters. Called by Batch and internally by View.
@@ -374,7 +375,7 @@ private:
     /// Intermediate light processing results.
     /// Intermediate light processing results.
     Vector<LightQueryResult> lightQueryResults_;
     Vector<LightQueryResult> lightQueryResults_;
     /// Info for scene render passes defined by the renderpath.
     /// Info for scene render passes defined by the renderpath.
-    Vector<ScenePassInfo> scenePasses_;
+    PODVector<ScenePassInfo> scenePasses_;
     /// Per-pixel light queues.
     /// Per-pixel light queues.
     Vector<LightBatchQueue> lightQueues_;
     Vector<LightBatchQueue> lightQueues_;
     /// Per-vertex light queues.
     /// Per-vertex light queues.

+ 12 - 1
Source/Atomic/IO/Deserializer.cpp

@@ -330,6 +330,9 @@ Variant Deserializer::ReadVariant(VariantType type)
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         return Variant(ReadVariantVector());
         return Variant(ReadVariantVector());
 
 
+    case VAR_STRINGVECTOR:
+        return Variant(ReadStringVector());
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         return Variant(ReadVariantMap());
         return Variant(ReadVariantMap());
 
 
@@ -347,7 +350,7 @@ Variant Deserializer::ReadVariant(VariantType type)
 
 
     case VAR_MATRIX4:
     case VAR_MATRIX4:
         return Variant(ReadMatrix4());
         return Variant(ReadMatrix4());
-        
+
     case VAR_DOUBLE:
     case VAR_DOUBLE:
         return Variant(ReadDouble());
         return Variant(ReadDouble());
 
 
@@ -364,6 +367,14 @@ VariantVector Deserializer::ReadVariantVector()
     return ret;
     return ret;
 }
 }
 
 
+StringVector Deserializer::ReadStringVector()
+{
+    StringVector ret(ReadVLE());
+    for (unsigned i = 0; i < ret.Size(); ++i)
+        ret[i] = ReadString();
+    return ret;
+}
+
 VariantMap Deserializer::ReadVariantMap()
 VariantMap Deserializer::ReadVariantMap()
 {
 {
     VariantMap ret;
     VariantMap ret;

+ 2 - 0
Source/Atomic/IO/Deserializer.h

@@ -122,6 +122,8 @@ public:
     Variant ReadVariant(VariantType type);
     Variant ReadVariant(VariantType type);
     /// Read a variant vector.
     /// Read a variant vector.
     VariantVector ReadVariantVector();
     VariantVector ReadVariantVector();
+    /// Read a string vector.
+    StringVector ReadStringVector();
     /// Read a variant map.
     /// Read a variant map.
     VariantMap ReadVariantMap();
     VariantMap ReadVariantMap();
     /// Read a variable-length encoded unsigned integer, which can use 29 bits maximum.
     /// Read a variable-length encoded unsigned integer, which can use 29 bits maximum.

+ 3 - 2
Source/Atomic/IO/File.cpp

@@ -56,6 +56,7 @@ static const char* openMode[] =
 #endif
 #endif
 
 
 #ifdef ANDROID
 #ifdef ANDROID
+const char* APK = "/apk/";
 static const unsigned READ_BUFFER_SIZE = 32768;
 static const unsigned READ_BUFFER_SIZE = 32768;
 #endif
 #endif
 static const unsigned SKIP_BUFFER_SIZE = 1024;
 static const unsigned SKIP_BUFFER_SIZE = 1024;
@@ -131,7 +132,7 @@ bool File::Open(const String& fileName, FileMode mode)
     }
     }
 
 
 #ifdef ANDROID
 #ifdef ANDROID
-    if (fileName.StartsWith("/apk/"))
+    if (IS_ASSET(fileName))
     {
     {
         if (mode != FILE_READ)
         if (mode != FILE_READ)
         {
         {
@@ -139,7 +140,7 @@ bool File::Open(const String& fileName, FileMode mode)
             return false;
             return false;
         }
         }
 
 
-        assetHandle_ = SDL_RWFromFile(fileName.Substring(5).CString(), "rb");
+        assetHandle_ = SDL_RWFromFile(ASSET(fileName), "rb");
         if (!assetHandle_)
         if (!assetHandle_)
         {
         {
             LOGERRORF("Could not open asset file %s", fileName.CString());
             LOGERRORF("Could not open asset file %s", fileName.CString());

+ 11 - 0
Source/Atomic/IO/File.h

@@ -34,6 +34,17 @@
 namespace Atomic
 namespace Atomic
 {
 {
 
 
+#ifdef ANDROID
+extern const char* APK;
+
+#define ASSET_DIR_INDICATOR "_"
+
+// Macro for checking if a given pathname is inside APK's assets directory
+#define IS_ASSET(p) p.StartsWith(APK)
+// Macro for truncating the APK prefix string from the asset pathname and at the same time patching the directory name components (see custom_rules.xml)
+#define ASSET(p) p.Substring(5).Replaced("/", ASSET_DIR_INDICATOR "/").CString()
+#endif
+
 /// File open mode.
 /// File open mode.
 enum FileMode
 enum FileMode
 {
 {

+ 74 - 79
Source/Atomic/IO/FileSystem.cpp

@@ -60,13 +60,17 @@
 #include <mach-o/dyld.h>
 #include <mach-o/dyld.h>
 #endif
 #endif
 
 
+extern "C"
+{
 #ifdef ANDROID
 #ifdef ANDROID
-extern "C" const char* SDL_Android_GetFilesDir();
-#endif
-#ifdef IOS
-extern "C" const char* SDL_IOS_GetResourceDir();
-extern "C" const char* SDL_IOS_GetDocumentsDir();
+const char* SDL_Android_GetFilesDir();
+char** SDL_Android_GetFileList(const char* path, int* count);
+void SDL_Android_FreeFileList(char*** array, int* count);
+#elif IOS
+const char* SDL_IOS_GetResourceDir();
+const char* SDL_IOS_GetDocumentsDir();
 #endif
 #endif
+}
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
@@ -81,7 +85,7 @@ int DoSystemCommand(const String& commandLine, bool redirectToLog, Context* cont
     // Get a platform-agnostic temporary file name for stderr redirection
     // Get a platform-agnostic temporary file name for stderr redirection
     String stderrFilename;
     String stderrFilename;
     String adjustedCommandLine(commandLine);
     String adjustedCommandLine(commandLine);
-    char* prefPath = SDL_GetPrefPath("urho3d", "temp");
+    char* prefPath = SDL_GetPrefPath("atomicgameengine", "temp");
     if (prefPath)
     if (prefPath)
     {
     {
         stderrFilename = String(prefPath) + "command-stderr";
         stderrFilename = String(prefPath) + "command-stderr";
@@ -559,12 +563,10 @@ bool FileSystem::FileExists(const String& fileName) const
     if (!CheckAccess(GetPath(fileName)))
     if (!CheckAccess(GetPath(fileName)))
         return false;
         return false;
 
 
-    String fixedName = GetNativePath(RemoveTrailingSlash(fileName));
-
 #ifdef ANDROID
 #ifdef ANDROID
-    if (fixedName.StartsWith("/apk/"))
+    if (IS_ASSET(fileName))
     {
     {
-        SDL_RWops* rwOps = SDL_RWFromFile(fileName.Substring(5).CString(), "rb");
+        SDL_RWops* rwOps = SDL_RWFromFile(ASSET(fileName), "rb");
         if (rwOps)
         if (rwOps)
         {
         {
             SDL_RWclose(rwOps);
             SDL_RWclose(rwOps);
@@ -575,6 +577,8 @@ bool FileSystem::FileExists(const String& fileName) const
     }
     }
 #endif
 #endif
 
 
+    String fixedName = GetNativePath(RemoveTrailingSlash(fileName));
+
 #ifdef WIN32
 #ifdef WIN32
     DWORD attributes = GetFileAttributesW(WString(fixedName).CString());
     DWORD attributes = GetFileAttributesW(WString(fixedName).CString());
     if (attributes == INVALID_FILE_ATTRIBUTES || attributes & FILE_ATTRIBUTE_DIRECTORY)
     if (attributes == INVALID_FILE_ATTRIBUTES || attributes & FILE_ATTRIBUTE_DIRECTORY)
@@ -602,9 +606,31 @@ bool FileSystem::DirExists(const String& pathName) const
     String fixedName = GetNativePath(RemoveTrailingSlash(pathName));
     String fixedName = GetNativePath(RemoveTrailingSlash(pathName));
 
 
 #ifdef ANDROID
 #ifdef ANDROID
-    /// \todo Actually check for existence, now true is always returned for directories within the APK
-    if (fixedName.StartsWith("/apk/"))
-        return true;
+    if (IS_ASSET(fixedName))
+    {
+        // Split the pathname into two components: the longest parent directory path and the last name component
+        String assetPath(ASSET((fixedName + '/')));
+        String parentPath;
+        unsigned pos = assetPath.FindLast('/', assetPath.Length() - 2);
+        if (pos != String::NPOS)
+        {
+            parentPath = assetPath.Substring(0, pos - 1);
+            assetPath = assetPath.Substring(pos + 1);
+        }
+        assetPath.Resize(assetPath.Length() - 1);
+
+        bool exist = false;
+        int count;
+        char** list = SDL_Android_GetFileList(parentPath.CString(), &count);
+        for (int i = 0; i < count; ++i)
+        {
+            exist = assetPath == list[i];
+            if (exist)
+                break;
+        }
+        SDL_Android_FreeFileList(&list, &count);
+        return exist;
+    }
 #endif
 #endif
 
 
 #ifdef WIN32
 #ifdef WIN32
@@ -640,7 +666,7 @@ String FileSystem::GetProgramDir() const
 #if defined(ANDROID)
 #if defined(ANDROID)
     // This is an internal directory specifier pointing to the assets in the .apk
     // This is an internal directory specifier pointing to the assets in the .apk
     // Files from this directory will be opened using special handling
     // Files from this directory will be opened using special handling
-    programDir_ = "/apk/";
+    programDir_ = APK;
     return programDir_;
     return programDir_;
 #elif defined(IOS)
 #elif defined(IOS)
     programDir_ = AddTrailingSlash(SDL_IOS_GetResourceDir());
     programDir_ = AddTrailingSlash(SDL_IOS_GetResourceDir());
@@ -744,81 +770,51 @@ bool FileSystem::SetLastModifiedTime(const String& fileName, unsigned newTime)
 #endif
 #endif
 }
 }
 
 
-#if defined(ANDROID)
-
-// the android asset manager is quite limited, so use a manifest instead
-static Vector<String> _atomicManifest;
-
 void FileSystem::ScanDirInternal(Vector<String>& result, String path, const String& startPath,
 void FileSystem::ScanDirInternal(Vector<String>& result, String path, const String& startPath,
     const String& filter, unsigned flags, bool recursive) const
     const String& filter, unsigned flags, bool recursive) const
 {
 {
-
-    if (!_atomicManifest.Size())
-    {
-        File file(context_, "/apk/AtomicManifest", FILE_READ);
-        if (!file.IsOpen())
-        {
-            LOGERRORF("Unabled to open AtomicManifest");
-            return;
-        }
-
-        String manifest = file.ReadString();
-        _atomicManifest = manifest.Split(';');
-    }
-
-    if (path.StartsWith("/apk/"))
-        path = path.Substring(5);
-
-    path.Replace(String("//"), String("/"));
-    path = RemoveTrailingSlash(path);
-
-    // first path is the CoreData/AtomicResources folder
-    path = path.Substring(path.Find('/') + 1) + "/";
+    path = AddTrailingSlash(path);
+    String deltaPath;
+    if (path.Length() > startPath.Length())
+        deltaPath = path.Substring(startPath.Length());
 
 
     String filterExtension = filter.Substring(filter.Find('.'));
     String filterExtension = filter.Substring(filter.Find('.'));
     if (filterExtension.Contains('*'))
     if (filterExtension.Contains('*'))
         filterExtension.Clear();
         filterExtension.Clear();
 
 
-    for (unsigned i = 0; i < _atomicManifest.Size(); i++ )
+#ifdef ANDROID
+    if (IS_ASSET(path))
     {
     {
-        const String& file = _atomicManifest[i];
-        String filePath = GetPath(file);
-        String filename = GetFileNameAndExtension(file);
-
-        //LOGINFOF("%s : %s : %s", path.CString(), filePath.CString(), filename.CString());
-
-        if (filePath.StartsWith(path))
+        String assetPath(ASSET(path));
+        assetPath.Resize(assetPath.Length() - 1);       // AssetManager.list() does not like trailing slash
+        int count;
+        char** list = SDL_Android_GetFileList(assetPath.CString(), &count);
+        for (int i = 0; i < count; ++i)
         {
         {
-            if (flags & SCAN_FILES)
-            {
-                if (filterExtension.Empty() || filename.EndsWith(filterExtension))
-                {
-                    String deltaPath;
-
-                    if (path.Length() > startPath.Length())
-                        deltaPath = path.Substring(startPath.Length());
+            String fileName(list[i]);
+            if (!(flags & SCAN_HIDDEN) && fileName.StartsWith("."))
+                continue;
 
 
-                    result.Push(deltaPath + GetFileNameAndExtension(file));
-                }
+            // Patch the directory name back after retrieving the directory flag
+            bool isDirectory = fileName.EndsWith(ASSET_DIR_INDICATOR);
+            if (isDirectory)
+            {
+                fileName.Resize(fileName.Length() - sizeof(ASSET_DIR_INDICATOR) / sizeof(char) + 1);
+                if (flags & SCAN_DIRS)
+                    result.Push(deltaPath + fileName);
+                if (recursive)
+                    ScanDirInternal(result, path + fileName, startPath, filter, flags, recursive);
+            }
+            else if (flags & SCAN_FILES)
+            {
+                if (filterExtension.Empty() || fileName.EndsWith(filterExtension))
+                    result.Push(deltaPath + fileName);
             }
             }
         }
         }
+        SDL_Android_FreeFileList(&list, &count);
+        return;
     }
     }
-}
-
-#else
-
-void FileSystem::ScanDirInternal(Vector<String>& result, String path, const String& startPath,
-    const String& filter, unsigned flags, bool recursive) const
-{
-    path = AddTrailingSlash(path);
-    String deltaPath;
-    if (path.Length() > startPath.Length())
-        deltaPath = path.Substring(startPath.Length());
-
-    String filterExtension = filter.Substring(filter.Find('.'));
-    if (filterExtension.Contains('*'))
-        filterExtension.Clear();
-
+#endif
 #ifdef WIN32
 #ifdef WIN32
     WIN32_FIND_DATAW info;
     WIN32_FIND_DATAW info;
     HANDLE handle = FindFirstFileW(WString(path + "*").CString(), &info);
     HANDLE handle = FindFirstFileW(WString(path + "*").CString(), &info);
@@ -844,11 +840,11 @@ void FileSystem::ScanDirInternal(Vector<String>& result, String path, const Stri
                         result.Push(deltaPath + fileName);
                         result.Push(deltaPath + fileName);
                 }
                 }
             }
             }
-        } while (FindNextFileW(handle, &info));
+        }
+        while (FindNextFileW(handle, &info));
 
 
         FindClose(handle);
         FindClose(handle);
     }
     }
-}
 #else
 #else
     DIR* dir;
     DIR* dir;
     struct dirent* de;
     struct dirent* de;
@@ -882,10 +878,9 @@ void FileSystem::ScanDirInternal(Vector<String>& result, String path, const Stri
         }
         }
         closedir(dir);
         closedir(dir);
     }
     }
-}
 #endif
 #endif
 
 
-#endif
+}
 
 
 bool FileSystem::CreateDirs(const String& root, const String& subdirectory)
 bool FileSystem::CreateDirs(const String& root, const String& subdirectory)
 {
 {

+ 2 - 2
Source/Atomic/IO/PackageFile.cpp

@@ -53,7 +53,7 @@ PackageFile::~PackageFile()
 bool PackageFile::Open(const String& fileName, unsigned startOffset)
 bool PackageFile::Open(const String& fileName, unsigned startOffset)
 {
 {
 #ifdef ANDROID
 #ifdef ANDROID
-    if (fileName.StartsWith("/apk/"))
+    if (IS_ASSET(fileName))
     {
     {
         LOGERROR("Package files within the apk are not supported on Android");
         LOGERROR("Package files within the apk are not supported on Android");
         return false;
         return false;
@@ -142,7 +142,7 @@ const PackageEntry* PackageFile::GetEntry(const String& fileName) const
     HashMap<String, PackageEntry>::ConstIterator i = entries_.Find(fileName);
     HashMap<String, PackageEntry>::ConstIterator i = entries_.Find(fileName);
     if (i != entries_.End())
     if (i != entries_.End())
         return &i->second_;
         return &i->second_;
-    
+
 #ifdef WIN32
 #ifdef WIN32
     // On Windows perform a fallback case-insensitive search
     // On Windows perform a fallback case-insensitive search
     else
     else

+ 13 - 1
Source/Atomic/IO/Serializer.cpp

@@ -282,6 +282,9 @@ bool Serializer::WriteVariantData(const Variant& value)
     case VAR_VARIANTVECTOR:
     case VAR_VARIANTVECTOR:
         return WriteVariantVector(value.GetVariantVector());
         return WriteVariantVector(value.GetVariantVector());
 
 
+    case VAR_STRINGVECTOR:
+        return WriteStringVector(value.GetStringVector());
+
     case VAR_VARIANTMAP:
     case VAR_VARIANTMAP:
         return WriteVariantMap(value.GetVariantMap());
         return WriteVariantMap(value.GetVariantMap());
 
 
@@ -299,7 +302,7 @@ bool Serializer::WriteVariantData(const Variant& value)
 
 
     case VAR_MATRIX4:
     case VAR_MATRIX4:
         return WriteMatrix4(value.GetMatrix4());
         return WriteMatrix4(value.GetMatrix4());
-        
+
     case VAR_DOUBLE:
     case VAR_DOUBLE:
         return WriteDouble(value.GetDouble());
         return WriteDouble(value.GetDouble());
 
 
@@ -317,6 +320,15 @@ bool Serializer::WriteVariantVector(const VariantVector& value)
     return success;
     return success;
 }
 }
 
 
+bool Serializer::WriteStringVector(const StringVector& value)
+{
+    bool success = true;
+    success &= WriteVLE(value.Size());
+    for (StringVector::ConstIterator i = value.Begin(); i != value.End(); ++i)
+        success &= WriteString(*i);
+    return success;
+}
+
 bool Serializer::WriteVariantMap(const VariantMap& value)
 bool Serializer::WriteVariantMap(const VariantMap& value)
 {
 {
     bool success = true;
     bool success = true;

+ 2 - 0
Source/Atomic/IO/Serializer.h

@@ -113,6 +113,8 @@ public:
     bool WriteVariantData(const Variant& value);
     bool WriteVariantData(const Variant& value);
     /// Write a variant vector.
     /// Write a variant vector.
     bool WriteVariantVector(const VariantVector& value);
     bool WriteVariantVector(const VariantVector& value);
+    /// Write a variant vector.
+    bool WriteStringVector(const StringVector& value);
     /// Write a variant map.
     /// Write a variant map.
     bool WriteVariantMap(const VariantMap& value);
     bool WriteVariantMap(const VariantMap& value);
     /// Write a variable-length encoded unsigned integer, which can use 29 bits maximum.
     /// Write a variable-length encoded unsigned integer, which can use 29 bits maximum.

+ 2 - 2
Source/Atomic/Input/Input.cpp

@@ -80,7 +80,7 @@ UIElement* TouchState::GetTouchedElement()
 #define EM_TRUE 1
 #define EM_TRUE 1
 
 
 /// Glue between Urho Input and Emscripten HTML5
 /// Glue between Urho Input and Emscripten HTML5
-/** HTML5 (Emscripten) is limited in the way it handles input. The EmscriptenInput class attempts to provide the glue between Urho3D Input behavior and HTML5, where SDL currently fails to do so.
+/** HTML5 (Emscripten) is limited in the way it handles input. The EmscriptenInput class attempts to provide the glue between Atomic Input behavior and HTML5, where SDL currently fails to do so.
  *
  *
  * Mouse Input:
  * Mouse Input:
  * - The OS mouse cursor position can't be set.
  * - The OS mouse cursor position can't be set.
@@ -1178,7 +1178,7 @@ void Input::ResetJoysticks()
     joysticks_.Clear();
     joysticks_.Clear();
 
 
     // Open each detected joystick automatically on startup
     // Open each detected joystick automatically on startup
-    int size = SDL_NumJoysticks();
+    unsigned size = static_cast<unsigned>(SDL_NumJoysticks());
     for (unsigned i = 0; i < size; ++i)
     for (unsigned i = 0; i < size; ++i)
         OpenJoystick(i);
         OpenJoystick(i);
 }
 }

+ 7 - 2
Source/Atomic/Math/AreaAllocator.cpp

@@ -24,6 +24,8 @@
 
 
 #include "../Math/AreaAllocator.h"
 #include "../Math/AreaAllocator.h"
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 
@@ -147,7 +149,7 @@ bool AreaAllocator::Allocate(int width, int height, int& x, int& y)
         // Remove the reserved area from all free areas
         // Remove the reserved area from all free areas
         for (unsigned i = 0; i < freeAreas_.Size();)
         for (unsigned i = 0; i < freeAreas_.Size();)
         {
         {
-            if (SplitRect(freeAreas_[i], reserved))
+            if (SplitRect(i, reserved))
                 freeAreas_.Erase(i);
                 freeAreas_.Erase(i);
             else
             else
                 ++i;
                 ++i;
@@ -159,8 +161,11 @@ bool AreaAllocator::Allocate(int width, int height, int& x, int& y)
     return true;
     return true;
 }
 }
 
 
-bool AreaAllocator::SplitRect(IntRect original, const IntRect& reserve)
+bool AreaAllocator::SplitRect(unsigned freeAreaIndex, const IntRect& reserve)
 {
 {
+    // Make a copy, as the vector will be modified
+    IntRect original = freeAreas_[freeAreaIndex];
+
     if (reserve.right_ > original.left_ && reserve.left_ < original.right_ && reserve.bottom_ > original.top_ &&
     if (reserve.right_ > original.left_ && reserve.left_ < original.right_ && reserve.bottom_ > original.top_ &&
         reserve.top_ < original.bottom_)
         reserve.top_ < original.bottom_)
     {
     {

+ 1 - 1
Source/Atomic/Math/AreaAllocator.h

@@ -54,7 +54,7 @@ public:
 
 
 private:
 private:
     /// Remove space from a free rectangle. Return true if the original rectangle should be erased from the free list. Not called in fast mode.
     /// Remove space from a free rectangle. Return true if the original rectangle should be erased from the free list. Not called in fast mode.
-    bool SplitRect(IntRect original, const IntRect& reserve);
+    bool SplitRect(unsigned freeAreaIndex, const IntRect& reserve);
     /// Clean up redundant free space. Not called in fast mode.
     /// Clean up redundant free space. Not called in fast mode.
     void Cleanup();
     void Cleanup();
 
 

+ 2 - 0
Source/Atomic/Math/BoundingBox.cpp

@@ -25,6 +25,8 @@
 #include "../Math/Frustum.h"
 #include "../Math/Frustum.h"
 #include "../Math/Polyhedron.h"
 #include "../Math/Polyhedron.h"
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 2 - 0
Source/Atomic/Math/Color.cpp

@@ -26,6 +26,8 @@
 
 
 #include <cstdio>
 #include <cstdio>
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 13 - 0
Source/Atomic/Math/Color.h

@@ -90,6 +90,16 @@ public:
     {
     {
     }
     }
 
 
+    /// Assign from another color.
+    Color& operator =(const Color& rhs)
+    {
+        r_ = rhs.r_;
+        g_ = rhs.g_;
+        b_ = rhs.b_;
+        a_ = rhs.a_;
+        return *this;
+    }
+
     /// Test for equality with another color without epsilon.
     /// Test for equality with another color without epsilon.
     bool operator ==(const Color& rhs) const { return r_ == rhs.r_ && g_ == rhs.g_ && b_ == rhs.b_ && a_ == rhs.a_; }
     bool operator ==(const Color& rhs) const { return r_ == rhs.r_ && g_ == rhs.g_ && b_ == rhs.b_ && a_ == rhs.a_; }
 
 
@@ -102,6 +112,9 @@ public:
     /// Add a color.
     /// Add a color.
     Color operator +(const Color& rhs) const { return Color(r_ + rhs.r_, g_ + rhs.g_, b_ + rhs.b_, a_ + rhs.a_); }
     Color operator +(const Color& rhs) const { return Color(r_ + rhs.r_, g_ + rhs.g_, b_ + rhs.b_, a_ + rhs.a_); }
 
 
+    /// Return negation.
+    Color operator -() const { return Color(-r_, -g_, -b_, -a_); }
+
     /// Substract a color.
     /// Substract a color.
     Color operator -(const Color& rhs) const { return Color(r_ - rhs.r_, g_ - rhs.g_, b_ - rhs.b_, a_ - rhs.a_); }
     Color operator -(const Color& rhs) const { return Color(r_ - rhs.r_, g_ - rhs.g_, b_ - rhs.b_, a_ - rhs.a_); }
 
 

+ 2 - 0
Source/Atomic/Math/Frustum.cpp

@@ -24,6 +24,8 @@
 
 
 #include "../Math/Frustum.h"
 #include "../Math/Frustum.h"
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 48 - 2
Source/Atomic/Math/MathDefs.h

@@ -62,6 +62,9 @@ inline bool Equals(float lhs, float rhs) { return lhs + M_EPSILON >= rhs && lhs
 /// Linear interpolation between two float values.
 /// Linear interpolation between two float values.
 inline float Lerp(float lhs, float rhs, float t) { return lhs * (1.0f - t) + rhs * t; }
 inline float Lerp(float lhs, float rhs, float t) { return lhs * (1.0f - t) + rhs * t; }
 
 
+/// Linear interpolation between two double values.
+inline double Lerp(double lhs, double rhs, float t) { return lhs * (1.0f - t) + rhs * t; }
+
 /// Return the smaller of two floats.
 /// Return the smaller of two floats.
 inline float Min(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; }
 inline float Min(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; }
 
 
@@ -189,12 +192,55 @@ inline float Random(float range) { return Rand() * range / 32767.0f; }
 inline float Random(float min, float max) { return Rand() * (max - min) / 32767.0f + min; }
 inline float Random(float min, float max) { return Rand() * (max - min) / 32767.0f + min; }
 
 
 /// Return a random integer between 0 and range - 1.
 /// Return a random integer between 0 and range - 1.
-inline int Random(int range) { return (Rand() * (range - 1) + 16384) / 32767; }
+inline int Random(int range) { return (int)(Random() * range); }
 
 
 /// Return a random integer between min and max - 1.
 /// Return a random integer between min and max - 1.
-inline int Random(int min, int max) { return (Rand() * (max - min - 1) + 16384) / 32767 + min; }
+inline int Random(int min, int max) { float range = (float)(max - min); return (int)(Random() * range) + min; }
 
 
 /// Return a random normal distributed number with the given mean value and variance.
 /// Return a random normal distributed number with the given mean value and variance.
 inline float RandomNormal(float meanValue, float variance) { return RandStandardNormal() * sqrtf(variance) + meanValue; }
 inline float RandomNormal(float meanValue, float variance) { return RandStandardNormal() * sqrtf(variance) + meanValue; }
 
 
+/// Convert float to half float. From https://gist.github.com/martinkallman/5049614
+inline unsigned short FloatToHalf(float value)
+{
+    unsigned inu = *((unsigned*)&value);
+    unsigned t1 = inu & 0x7fffffff;         // Non-sign bits
+    unsigned t2 = inu & 0x80000000;         // Sign bit
+    unsigned t3 = inu & 0x7f800000;         // Exponent
+
+    t1 >>= 13;                              // Align mantissa on MSB
+    t2 >>= 16;                              // Shift sign bit into position
+
+    t1 -= 0x1c000;                          // Adjust bias
+
+    t1 = (t3 < 0x38800000) ? 0 : t1;        // Flush-to-zero
+    t1 = (t3 > 0x47000000) ? 0x7bff : t1;   // Clamp-to-max
+    t1 = (t3 == 0 ? 0 : t1);                // Denormals-as-zero
+
+    t1 |= t2;                               // Re-insert sign bit
+
+    return (unsigned short)t1;
+}
+
+/// Convert half float to float. From https://gist.github.com/martinkallman/5049614
+inline float HalfToFloat(unsigned short value)
+{
+    unsigned t1 = value & 0x7fff;           // Non-sign bits
+    unsigned t2 = value & 0x8000;           // Sign bit
+    unsigned t3 = value & 0x7c00;           // Exponent
+
+    t1 <<= 13;                              // Align mantissa on MSB
+    t2 <<= 16;                              // Shift sign bit into position
+
+    t1 += 0x38000000;                       // Adjust bias
+
+    t1 = (t3 == 0 ? 0 : t1);                // Denormals-as-zero
+
+    t1 |= t2;                               // Re-insert sign bit
+
+    float out;
+    *((unsigned*)&out) = t1;
+    return out;
+}
+
 }
 }

+ 2 - 0
Source/Atomic/Math/Plane.cpp

@@ -24,6 +24,8 @@
 
 
 #include "../Math/Plane.h"
 #include "../Math/Plane.h"
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 9 - 0
Source/Atomic/Math/Plane.h

@@ -63,6 +63,15 @@ public:
         Define(plane);
         Define(plane);
     }
     }
 
 
+    /// Assign from another plane.
+    Plane& operator =(const Plane& rhs)
+    {
+        normal_ = rhs.normal_;
+        absNormal_ = rhs.absNormal_;
+        d_ = rhs.d_;
+        return *this;
+    }
+
     /// Define from 3 vertices.
     /// Define from 3 vertices.
     void Define(const Vector3& v0, const Vector3& v1, const Vector3& v2)
     void Define(const Vector3& v0, const Vector3& v1, const Vector3& v2)
     {
     {

+ 6 - 0
Source/Atomic/Math/Polyhedron.cpp

@@ -25,6 +25,12 @@
 #include "../Math/Frustum.h"
 #include "../Math/Frustum.h"
 #include "../Math/Polyhedron.h"
 #include "../Math/Polyhedron.h"
 
 
+#include "../DebugNew.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:6293)
+#endif
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 7 - 0
Source/Atomic/Math/Polyhedron.h

@@ -69,6 +69,13 @@ public:
     /// Destruct.
     /// Destruct.
     ~Polyhedron();
     ~Polyhedron();
 
 
+    /// Assign from another polyhedron.
+    Polyhedron& operator =(const Polyhedron& rhs)
+    {
+        faces_ = rhs.faces_;
+        return *this;
+    }
+
     /// Define from a bounding box.
     /// Define from a bounding box.
     void Define(const BoundingBox& box);
     void Define(const BoundingBox& box);
     /// Define from a frustum.
     /// Define from a frustum.

+ 14 - 5
Source/Atomic/Math/Quaternion.cpp

@@ -26,6 +26,8 @@
 
 
 #include <cstdio>
 #include <cstdio>
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 
@@ -148,13 +150,20 @@ void Quaternion::FromRotationMatrix(const Matrix3& matrix)
 
 
 bool Quaternion::FromLookRotation(const Vector3& direction, const Vector3& upDirection)
 bool Quaternion::FromLookRotation(const Vector3& direction, const Vector3& upDirection)
 {
 {
+    Quaternion ret;
     Vector3 forward = direction.Normalized();
     Vector3 forward = direction.Normalized();
-    Vector3 v = forward.CrossProduct(upDirection).Normalized();
-    Vector3 up = v.CrossProduct(forward);
-    Vector3 right = up.CrossProduct(forward);
 
 
-    Quaternion ret;
-    ret.FromAxes(right, up, forward);
+    Vector3 v = forward.CrossProduct(upDirection);
+    // If direction & upDirection are parallel and crossproduct becomes zero, use FromRotationTo() fallback
+    if (v.LengthSquared() >= M_EPSILON)
+    {
+        v.Normalize();
+        Vector3 up = v.CrossProduct(forward);
+        Vector3 right = up.CrossProduct(forward);
+        ret.FromAxes(right, up, forward);
+    }
+    else
+        ret.FromRotationTo(Vector3::FORWARD, forward);
 
 
     if (!ret.IsNaN())
     if (!ret.IsNaN())
     {
     {

+ 2 - 0
Source/Atomic/Math/Random.cpp

@@ -24,6 +24,8 @@
 
 
 #include "../Math/Random.h"
 #include "../Math/Random.h"
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 2 - 0
Source/Atomic/Math/Ray.cpp

@@ -26,6 +26,8 @@
 #include "../Math/Frustum.h"
 #include "../Math/Frustum.h"
 #include "../Math/Ray.h"
 #include "../Math/Ray.h"
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 2 - 0
Source/Atomic/Math/Rect.cpp

@@ -26,6 +26,8 @@
 
 
 #include <cstdio>
 #include <cstdio>
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 2 - 0
Source/Atomic/Math/Sphere.cpp

@@ -25,6 +25,8 @@
 #include "../Math/Frustum.h"
 #include "../Math/Frustum.h"
 #include "../Math/Polyhedron.h"
 #include "../Math/Polyhedron.h"
 
 
+#include "../DebugNew.h"
+
 namespace Atomic
 namespace Atomic
 {
 {
 
 

+ 8 - 0
Source/Atomic/Math/Vector2.h

@@ -253,6 +253,14 @@ public:
     {
     {
     }
     }
 
 
+    /// Assign from another vector.
+    IntVector2& operator =(const IntVector2& rhs)
+    {
+        x_ = rhs.x_;
+        y_ = rhs.y_;
+        return *this;
+    }
+
     /// Test for equality with another vector.
     /// Test for equality with another vector.
     bool operator ==(const IntVector2& rhs) const { return x_ == rhs.x_ && y_ == rhs.y_; }
     bool operator ==(const IntVector2& rhs) const { return x_ == rhs.x_ && y_ == rhs.y_; }
 
 

+ 311 - 198
Source/Atomic/Navigation/CrowdAgent.cpp

@@ -22,19 +22,15 @@
 
 
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
-#include "../Scene/Component.h"
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Navigation/CrowdAgent.h"
+#include "../Core/Profiler.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DebugRenderer.h"
-#include "../Navigation/DetourCrowdManager.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/MemoryBuffer.h"
 #include "../Navigation/NavigationEvents.h"
 #include "../Navigation/NavigationEvents.h"
+#include "../Navigation/CrowdAgent.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
-#include "../Core/Profiler.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
-#include "../Scene/Serializable.h"
-#include "../Core/Variant.h"
 
 
 #include <Detour/include/DetourCommon.h>
 #include <Detour/include/DetourCommon.h>
 #include <DetourCrowd/include/DetourCrowd.h>
 #include <DetourCrowd/include/DetourCrowd.h>
@@ -46,42 +42,54 @@ namespace Atomic
 
 
 extern const char* NAVIGATION_CATEGORY;
 extern const char* NAVIGATION_CATEGORY;
 
 
-static const unsigned DEFAULT_AGENT_NAVIGATION_FILTER_TYPE = 0;
+static const CrowdAgentRequestedTarget DEFAULT_AGENT_REQUEST_TARGET_TYPE = CA_REQUESTEDTARGET_NONE;
 static const float DEFAULT_AGENT_MAX_SPEED = 0.f;
 static const float DEFAULT_AGENT_MAX_SPEED = 0.f;
 static const float DEFAULT_AGENT_MAX_ACCEL = 0.f;
 static const float DEFAULT_AGENT_MAX_ACCEL = 0.f;
+static const unsigned DEFAULT_AGENT_QUERY_FILTER_TYPE = 0;
+static const unsigned DEFAULT_AGENT_OBSTACLE_AVOIDANCE_TYPE = 0;
 static const NavigationQuality DEFAULT_AGENT_AVOIDANCE_QUALITY = NAVIGATIONQUALITY_HIGH;
 static const NavigationQuality DEFAULT_AGENT_AVOIDANCE_QUALITY = NAVIGATIONQUALITY_HIGH;
-static const NavigationPushiness DEFAULT_AGENT_NAVIGATION_PUSHINESS = PUSHINESS_MEDIUM;
+static const NavigationPushiness DEFAULT_AGENT_NAVIGATION_PUSHINESS = NAVIGATIONPUSHINESS_MEDIUM;
+
+static const unsigned SCOPE_NAVIGATION_QUALITY_PARAMS = 1;
+static const unsigned SCOPE_NAVIGATION_PUSHINESS_PARAMS = 2;
+static const unsigned SCOPE_BASE_PARAMS = M_MAX_UNSIGNED & ~SCOPE_NAVIGATION_QUALITY_PARAMS & ~SCOPE_NAVIGATION_PUSHINESS_PARAMS;
 
 
-const char* crowdAgentAvoidanceQualityNames[] = {
+static const char* crowdAgentRequestedTargetTypeNames[] = {
+    "none",
+    "position",
+    "velocity",
+    0
+};
+
+static const char* crowdAgentAvoidanceQualityNames[] = {
     "low",
     "low",
     "medium",
     "medium",
     "high",
     "high",
     0
     0
 };
 };
 
 
-const char* crowdAgentPushinessNames[] = {
+static const char* crowdAgentPushinessNames[] = {
     "low",
     "low",
     "medium",
     "medium",
     "high",
     "high",
     0
     0
 };
 };
 
 
-
 CrowdAgent::CrowdAgent(Context* context) :
 CrowdAgent::CrowdAgent(Context* context) :
     Component(context),
     Component(context),
-    inCrowd_(false),
     agentCrowdId_(-1),
     agentCrowdId_(-1),
-    targetRef_(-1),
+    requestedTargetType_(DEFAULT_AGENT_REQUEST_TARGET_TYPE),
     updateNodePosition_(true),
     updateNodePosition_(true),
     maxAccel_(DEFAULT_AGENT_MAX_ACCEL),
     maxAccel_(DEFAULT_AGENT_MAX_ACCEL),
     maxSpeed_(DEFAULT_AGENT_MAX_SPEED),
     maxSpeed_(DEFAULT_AGENT_MAX_SPEED),
     radius_(0.0f),
     radius_(0.0f),
     height_(0.0f),
     height_(0.0f),
-    filterType_(DEFAULT_AGENT_NAVIGATION_FILTER_TYPE),
+    queryFilterType_(DEFAULT_AGENT_QUERY_FILTER_TYPE),
+    obstacleAvoidanceType_(DEFAULT_AGENT_OBSTACLE_AVOIDANCE_TYPE),
     navQuality_(DEFAULT_AGENT_AVOIDANCE_QUALITY),
     navQuality_(DEFAULT_AGENT_AVOIDANCE_QUALITY),
     navPushiness_(DEFAULT_AGENT_NAVIGATION_PUSHINESS),
     navPushiness_(DEFAULT_AGENT_NAVIGATION_PUSHINESS),
-    previousTargetState_(CROWD_AGENT_TARGET_NONE),
-    previousAgentState_(CROWD_AGENT_READY),
+    previousTargetState_(CA_TARGET_NONE),
+    previousAgentState_(CA_STATE_WALKING),
     ignoreTransformChanges_(false)
     ignoreTransformChanges_(false)
 {
 {
 }
 }
@@ -95,43 +103,58 @@ void CrowdAgent::RegisterObject(Context* context)
 {
 {
     context->RegisterFactory<CrowdAgent>(NAVIGATION_CATEGORY);
     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("Target Position", GetTargetPosition, SetMoveTarget, Vector3, Vector3::ZERO, 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)
-        node->AddListener(this);
-}
-
-void CrowdAgent::OnSceneSet(Scene* scene)
-{
-    if (scene)
+    ATTRIBUTE("Target Position", Vector3, targetPosition_, Vector3::ZERO, AM_DEFAULT);
+    ATTRIBUTE("Target Velocity", Vector3, targetVelocity_, Vector3::ZERO, AM_DEFAULT);
+    ENUM_ATTRIBUTE("Requested Target Type", requestedTargetType_, crowdAgentRequestedTargetTypeNames,
+        DEFAULT_AGENT_REQUEST_TARGET_TYPE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Update Node Position", GetUpdateNodePosition, SetUpdateNodePosition, bool, true, AM_DEFAULT);
+    ATTRIBUTE("Max Accel", float, maxAccel_, DEFAULT_AGENT_MAX_ACCEL, AM_DEFAULT);
+    ATTRIBUTE("Max Speed", float, maxSpeed_, DEFAULT_AGENT_MAX_SPEED, AM_DEFAULT);
+    ATTRIBUTE("Radius", float, radius_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE("Height", float, height_, 0.0f, AM_DEFAULT);
+    ATTRIBUTE("Query Filter Type", unsigned, queryFilterType_, DEFAULT_AGENT_QUERY_FILTER_TYPE, AM_DEFAULT);
+    ATTRIBUTE("Obstacle Avoidance Type", unsigned, obstacleAvoidanceType_, DEFAULT_AGENT_OBSTACLE_AVOIDANCE_TYPE, AM_DEFAULT);
+    ENUM_ATTRIBUTE("Navigation Pushiness", navPushiness_, crowdAgentPushinessNames, DEFAULT_AGENT_NAVIGATION_PUSHINESS, AM_DEFAULT);
+    ENUM_ATTRIBUTE("Navigation Quality", navQuality_, crowdAgentAvoidanceQualityNames, DEFAULT_AGENT_AVOIDANCE_QUALITY, AM_DEFAULT);
+}
+
+void CrowdAgent::ApplyAttributes()
+{
+    // Values from Editor, saved-file, or network must be checked before applying
+    maxAccel_ = Max(0.f, maxAccel_);
+    maxSpeed_ = Max(0.f, maxSpeed_);
+    radius_ = Max(0.f, radius_);
+    height_ = Max(0.f, height_);
+    queryFilterType_ = Min(queryFilterType_, DT_CROWD_MAX_QUERY_FILTER_TYPE - 1);
+    obstacleAvoidanceType_ = Min(obstacleAvoidanceType_, DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS - 1);
+
+    UpdateParameters();
+
+    // Set or reset target after we have attributes applied to the agent's parameters.
+    CrowdAgentRequestedTarget requestedTargetType = requestedTargetType_;
+    if (CA_REQUESTEDTARGET_NONE != requestedTargetType_)
     {
     {
-        if (scene == node_)
-            LOGERROR(GetTypeName() + " should not be created to the root scene node");
-        crowdManager_ = scene->GetOrCreateComponent<DetourCrowdManager>();
-        AddAgentToCrowd();
+        // Assign a dummy value such that the value check in the setter method passes
+        requestedTargetType_ = CA_REQUESTEDTARGET_NONE;
+        if (requestedTargetType == CA_REQUESTEDTARGET_POSITION)
+            SetTargetPosition(targetPosition_);
+        else
+            SetTargetVelocity(targetVelocity_);
     }
     }
     else
     else
-        RemoveAgentFromCrowd();
+    {
+        requestedTargetType_ = CA_REQUESTEDTARGET_POSITION;
+        ResetTarget();
+    }
 }
 }
 
 
 void CrowdAgent::OnSetEnabled()
 void CrowdAgent::OnSetEnabled()
 {
 {
     bool enabled = IsEnabledEffective();
     bool enabled = IsEnabledEffective();
 
 
-    if (enabled && !inCrowd_)
+    if (enabled && !IsInCrowd())
         AddAgentToCrowd();
         AddAgentToCrowd();
-    else if (!enabled && inCrowd_)
+    else if (!enabled && IsInCrowd())
         RemoveAgentFromCrowd();
         RemoveAgentFromCrowd();
 }
 }
 
 
@@ -161,44 +184,112 @@ void CrowdAgent::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
     }
     }
 }
 }
 
 
-const dtCrowdAgent* CrowdAgent::GetDetourCrowdAgent() const
+void CrowdAgent::UpdateParameters(unsigned scope)
 {
 {
-    return crowdManager_ && inCrowd_ ? crowdManager_->GetCrowdAgent(agentCrowdId_) : 0;
+    const dtCrowdAgent* agent = GetDetourCrowdAgent();
+    if (agent)
+    {
+        dtCrowdAgentParams params = agent->params;
+
+        if (scope & SCOPE_NAVIGATION_QUALITY_PARAMS)
+        {
+            switch (navQuality_)
+            {
+            case NAVIGATIONQUALITY_LOW:
+                params.updateFlags = 0
+                                     | DT_CROWD_OPTIMIZE_VIS
+                                     | DT_CROWD_ANTICIPATE_TURNS;
+                break;
+
+            case NAVIGATIONQUALITY_MEDIUM:
+                params.updateFlags = 0
+                                     | DT_CROWD_OPTIMIZE_TOPO
+                                     | DT_CROWD_OPTIMIZE_VIS
+                                     | DT_CROWD_ANTICIPATE_TURNS
+                                     | DT_CROWD_SEPARATION;
+                break;
+
+            case NAVIGATIONQUALITY_HIGH:
+                params.updateFlags = 0
+                                     // Path finding
+                                     | DT_CROWD_OPTIMIZE_TOPO
+                                     | DT_CROWD_OPTIMIZE_VIS
+                                     // Steering
+                                     | DT_CROWD_ANTICIPATE_TURNS
+                                     | DT_CROWD_SEPARATION
+                                     // Velocity planning
+                                     | DT_CROWD_OBSTACLE_AVOIDANCE;
+                break;
+            }
+        }
+
+        if (scope & SCOPE_NAVIGATION_PUSHINESS_PARAMS)
+        {
+            switch (navPushiness_)
+            {
+            case NAVIGATIONPUSHINESS_LOW:
+                params.separationWeight = 4.0f;
+                params.collisionQueryRange = radius_ * 16.0f;
+                break;
+
+            case NAVIGATIONPUSHINESS_MEDIUM:
+                params.separationWeight = 2.0f;
+                params.collisionQueryRange = radius_ * 8.0f;
+                break;
+
+            case NAVIGATIONPUSHINESS_HIGH:
+                params.separationWeight = 0.5f;
+                params.collisionQueryRange = radius_ * 1.0f;
+                break;
+            }
+        }
+
+        if (scope & SCOPE_BASE_PARAMS)
+        {
+            params.radius = radius_;
+            params.height = height_;
+            params.maxAcceleration = maxAccel_;
+            params.maxSpeed = maxSpeed_;
+            params.pathOptimizationRange = radius_ * 30.0f;
+            params.queryFilterType = (unsigned char)queryFilterType_;
+            params.obstacleAvoidanceType = (unsigned char)obstacleAvoidanceType_;
+        }
+
+        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+    }
 }
 }
 
 
-void CrowdAgent::AddAgentToCrowd()
+int CrowdAgent::AddAgentToCrowd(bool force)
 {
 {
-    if (!crowdManager_ || !crowdManager_->crowd_ || !node_)
-        return;
+    if (!node_ || !crowdManager_ || !crowdManager_->crowd_)
+        return -1;
 
 
-    PROFILE(AddAgentToCrowd);
-
-    if (!inCrowd_)
+    if (force || !IsInCrowd())
     {
     {
-        inCrowd_ = true;
+        PROFILE(AddAgentToCrowd);
+
         agentCrowdId_ = crowdManager_->AddAgent(this, node_->GetPosition());
         agentCrowdId_ = crowdManager_->AddAgent(this, node_->GetPosition());
         if (agentCrowdId_ == -1)
         if (agentCrowdId_ == -1)
-        {
-            inCrowd_ = false;
-            LOGERROR("AddAgentToCrowd: Could not add agent to crowd");
-            return;
-        }
-        crowdManager_->UpdateAgentNavigationQuality(this, navQuality_);
-        crowdManager_->UpdateAgentPushiness(this, navPushiness_);
+            return -1;
+
+        ApplyAttributes();
+
         previousAgentState_ = GetAgentState();
         previousAgentState_ = GetAgentState();
         previousTargetState_ = GetTargetState();
         previousTargetState_ = GetTargetState();
 
 
         // Agent created, but initial state is invalid and needs to be addressed
         // Agent created, but initial state is invalid and needs to be addressed
-        if (previousAgentState_ == CROWD_AGENT_INVALID)
+        if (previousAgentState_ == CA_STATE_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);
+            using namespace CrowdAgentFailure;
+
+            VariantMap& map = GetEventDataMap();
+            map[P_NODE] = GetNode();
+            map[P_CROWD_AGENT] = this;
+            map[P_CROWD_TARGET_STATE] = previousTargetState_;
+            map[P_CROWD_AGENT_STATE] = previousAgentState_;
+            map[P_POSITION] = GetPosition();
+            map[P_VELOCITY] = GetActualVelocity();
+            crowdManager_->SendEvent(E_CROWD_AGENT_FAILURE, map);
 
 
             // Reevaluate states as handling of event may have resulted in changes
             // Reevaluate states as handling of event may have resulted in changes
             previousAgentState_ = GetAgentState();
             previousAgentState_ = GetAgentState();
@@ -208,133 +299,162 @@ void CrowdAgent::AddAgentToCrowd()
         // Save the initial position to prevent CrowdAgentReposition event being triggered unnecessarily
         // Save the initial position to prevent CrowdAgentReposition event being triggered unnecessarily
         previousPosition_ = GetPosition();
         previousPosition_ = GetPosition();
     }
     }
+
+    return agentCrowdId_;
 }
 }
 
 
 void CrowdAgent::RemoveAgentFromCrowd()
 void CrowdAgent::RemoveAgentFromCrowd()
 {
 {
-    if (crowdManager_ && agentCrowdId_ != -1 && inCrowd_)
+    if (IsInCrowd())
     {
     {
         crowdManager_->RemoveAgent(this);
         crowdManager_->RemoveAgent(this);
-        inCrowd_ = false;
         agentCrowdId_ = -1;
         agentCrowdId_ = -1;
     }
     }
 }
 }
 
 
-void CrowdAgent::SetNavigationFilterType(unsigned filterType)
+void CrowdAgent::SetTargetPosition(const Vector3& position)
 {
 {
-    filterType_ = filterType;
-    if (crowdManager_ && inCrowd_)
+    if (position != targetPosition_ || CA_REQUESTEDTARGET_POSITION != requestedTargetType_)
     {
     {
-        // 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);
+        targetPosition_ = position;
+        requestedTargetType_ = CA_REQUESTEDTARGET_POSITION;
         MarkNetworkUpdate();
         MarkNetworkUpdate();
+
+        if (!IsInCrowd())
+            AddAgentToCrowd();
+        if (IsInCrowd())   // Make sure the previous method call is successful
+        {
+            dtPolyRef nearestRef;
+            Vector3 nearestPos = crowdManager_->FindNearestPoint(position, queryFilterType_, &nearestRef);
+            crowdManager_->GetCrowd()->requestMoveTarget(agentCrowdId_, nearestRef, nearestPos.Data());
+        }
     }
     }
 }
 }
 
 
-void CrowdAgent::SetMoveTarget(const Vector3& position)
+void CrowdAgent::SetTargetVelocity(const Vector3& velocity)
 {
 {
-    if (crowdManager_) {
-        if (!inCrowd_)
-            AddAgentToCrowd();
-        targetPosition_ = position;
-        if (crowdManager_->SetAgentTarget(this, position, targetRef_))
-            MarkNetworkUpdate();
+    if (velocity != targetVelocity_ || CA_REQUESTEDTARGET_VELOCITY != requestedTargetType_)
+    {
+        targetVelocity_ = velocity;
+        requestedTargetType_ = CA_REQUESTEDTARGET_VELOCITY;
+        MarkNetworkUpdate();
+
+        if (IsInCrowd())
+            crowdManager_->GetCrowd()->requestMoveVelocity(agentCrowdId_, velocity.Data());
     }
     }
 }
 }
 
 
-void CrowdAgent::ResetMoveTarget()
+void CrowdAgent::ResetTarget()
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (agent && agent->active)
+    if (CA_REQUESTEDTARGET_NONE != requestedTargetType_)
     {
     {
-        targetPosition_ = Vector3::ZERO;
-        crowdManager_->GetCrowd()->resetMoveTarget(agentCrowdId_);
+        requestedTargetType_ = CA_REQUESTEDTARGET_NONE;
         MarkNetworkUpdate();
         MarkNetworkUpdate();
+
+        if (IsInCrowd())
+            crowdManager_->GetCrowd()->resetMoveTarget(agentCrowdId_);
     }
     }
 }
 }
 
 
-void CrowdAgent::SetMoveVelocity(const Vector3& velocity)
+void CrowdAgent::SetUpdateNodePosition(bool unodepos)
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (agent && agent->active)
+    if (unodepos != updateNodePosition_)
     {
     {
-        crowdManager_->GetCrowd()->requestMoveVelocity(agentCrowdId_, velocity.Data());
+        updateNodePosition_ = unodepos;
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
 
 
-void CrowdAgent::SetMaxSpeed(float speed)
+void CrowdAgent::SetMaxAccel(float maxAccel)
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (agent)
+    if (maxAccel != maxAccel_ && maxAccel >= 0.f)
     {
     {
-        maxSpeed_ = speed;
-        dtCrowdAgentParams params = agent->params;
-        params.maxSpeed = speed;
-        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        maxAccel_ = maxAccel;
+        UpdateParameters(SCOPE_BASE_PARAMS);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
 
 
-void CrowdAgent::SetMaxAccel(float accel)
+void CrowdAgent::SetMaxSpeed(float maxSpeed)
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (agent)
+    if (maxSpeed != maxSpeed_ && maxSpeed >= 0.f)
     {
     {
-        maxAccel_ = accel;
-        dtCrowdAgentParams params = agent->params;
-        params.maxAcceleration = accel;
-        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        maxSpeed_ = maxSpeed;
+        UpdateParameters(SCOPE_BASE_PARAMS);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
 
 
 void CrowdAgent::SetRadius(float radius)
 void CrowdAgent::SetRadius(float radius)
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (agent)
+    if (radius != radius_ && radius > 0.f)
     {
     {
         radius_ = radius;
         radius_ = radius;
-        dtCrowdAgentParams params = agent->params;
-        params.radius = radius;
-        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        UpdateParameters(SCOPE_BASE_PARAMS | SCOPE_NAVIGATION_PUSHINESS_PARAMS);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
 
 
 void CrowdAgent::SetHeight(float height)
 void CrowdAgent::SetHeight(float height)
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (agent)
+    if (height != height_ && height > 0.f)
     {
     {
         height_ = height;
         height_ = height;
-        dtCrowdAgentParams params = agent->params;
-        params.height = height;
-        crowdManager_->GetCrowd()->updateAgentParameters(agentCrowdId_, &params);
+        UpdateParameters(SCOPE_BASE_PARAMS);
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdAgent::SetQueryFilterType(unsigned queryFilterType)
+{
+    if (queryFilterType != queryFilterType_)
+    {
+        if (queryFilterType >= DT_CROWD_MAX_QUERY_FILTER_TYPE)
+        {
+            LOGERRORF("The specified filter type index (%d) exceeds the maximum allowed value (%d)", queryFilterType,
+                DT_CROWD_MAX_QUERY_FILTER_TYPE);
+            return;
+        }
+
+        queryFilterType_ = queryFilterType;
+        UpdateParameters(SCOPE_BASE_PARAMS);
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdAgent::SetObstacleAvoidanceType(unsigned obstacleAvoidanceType)
+{
+    if (obstacleAvoidanceType != obstacleAvoidanceType_)
+    {
+        if (obstacleAvoidanceType >= DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS)
+        {
+            LOGERRORF("The specified obstacle avoidance type index (%d) exceeds the maximum allowed value (%d)",
+                obstacleAvoidanceType, DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS);
+            return;
+        }
+
+        obstacleAvoidanceType_ = obstacleAvoidanceType;
+        UpdateParameters(SCOPE_BASE_PARAMS);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
 
 
 void CrowdAgent::SetNavigationQuality(NavigationQuality val)
 void CrowdAgent::SetNavigationQuality(NavigationQuality val)
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (agent)
+    if (val != navQuality_)
     {
     {
         navQuality_ = val;
         navQuality_ = val;
-        crowdManager_->UpdateAgentNavigationQuality(this, navQuality_);
+        UpdateParameters(SCOPE_NAVIGATION_QUALITY_PARAMS);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
 
 
 void CrowdAgent::SetNavigationPushiness(NavigationPushiness val)
 void CrowdAgent::SetNavigationPushiness(NavigationPushiness val)
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (agent)
+    if (val != navPushiness_)
     {
     {
         navPushiness_ = val;
         navPushiness_ = val;
-        crowdManager_->UpdateAgentPushiness(this, navPushiness_);
+        UpdateParameters(SCOPE_NAVIGATION_PUSHINESS_PARAMS);
         MarkNetworkUpdate();
         MarkNetworkUpdate();
     }
     }
 }
 }
@@ -342,64 +462,70 @@ void CrowdAgent::SetNavigationPushiness(NavigationPushiness val)
 Vector3 CrowdAgent::GetPosition() const
 Vector3 CrowdAgent::GetPosition() const
 {
 {
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    return agent && agent->active ? Vector3(agent->npos) : node_->GetPosition();
+    return agent ? Vector3(agent->npos) : node_->GetPosition();
 }
 }
 
 
 Vector3 CrowdAgent::GetDesiredVelocity() const
 Vector3 CrowdAgent::GetDesiredVelocity() const
 {
 {
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    return agent && agent->active ? Vector3(agent->dvel) : Vector3::ZERO;
+    return agent ? Vector3(agent->dvel) : Vector3::ZERO;
 }
 }
 
 
 Vector3 CrowdAgent::GetActualVelocity() const
 Vector3 CrowdAgent::GetActualVelocity() const
 {
 {
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    return agent && agent->active ? Vector3(agent->vel) : Vector3::ZERO;
+    return agent ? Vector3(agent->vel) : Vector3::ZERO;
 }
 }
 
 
-Atomic::CrowdAgentState CrowdAgent::GetAgentState() const
+CrowdAgentState CrowdAgent::GetAgentState() const
 {
 {
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    return agent && agent->active ? (CrowdAgentState)agent->state : CROWD_AGENT_INVALID;
+    return agent ? (CrowdAgentState)agent->state : CA_STATE_INVALID;
 }
 }
 
 
-Atomic::CrowdTargetState CrowdAgent::GetTargetState() const
+CrowdAgentTargetState CrowdAgent::GetTargetState() const
 {
 {
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    return agent && agent->active ? (CrowdTargetState)agent->targetState : CROWD_AGENT_TARGET_NONE;
+    return agent ? (CrowdAgentTargetState)agent->targetState : CA_TARGET_NONE;
 }
 }
 
 
 bool CrowdAgent::HasArrived() const
 bool CrowdAgent::HasArrived() const
 {
 {
-    // Is the agent at or near the end of its path?
+    // Is the agent at or near the end of its path and within its own radius of the goal?
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
     const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    return agent && agent->active && (!agent->ncorners ||
-        (agent->cornerFlags[agent->ncorners - 1] & DT_STRAIGHTPATH_END &&
-            Equals(dtVdist2D(agent->npos, &agent->cornerVerts[(agent->ncorners - 1) * 3]), 0.f)));
+    return agent && (!agent->ncorners || (agent->cornerFlags[agent->ncorners - 1] & DT_STRAIGHTPATH_END &&
+                                          dtVdist2D(agent->npos, &agent->cornerVerts[(agent->ncorners - 1) * 3]) <=
+                                          agent->params.radius));
 }
 }
 
 
-void CrowdAgent::SetUpdateNodePosition(bool unodepos)
+bool CrowdAgent::IsInCrowd() const
 {
 {
-    updateNodePosition_ = unodepos;
-    MarkNetworkUpdate();
+    return crowdManager_ && agentCrowdId_ != -1;
 }
 }
 
 
-void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newVel)
+void CrowdAgent::OnCrowdUpdate(dtCrowdAgent* ag, float dt)
 {
 {
+    assert (ag);
     if (node_)
     if (node_)
     {
     {
+        Vector3 newPos(ag->npos);
+        Vector3 newVel(ag->vel);
+
         // Notify parent node of the reposition
         // Notify parent node of the reposition
         if (newPos != previousPosition_)
         if (newPos != previousPosition_)
         {
         {
             previousPosition_ = newPos;
             previousPosition_ = newPos;
 
 
-            VariantMap& map = GetContext()->GetEventDataMap();
-            map[CrowdAgentReposition::P_NODE] = GetNode();
-            map[CrowdAgentReposition::P_CROWD_AGENT] = this;
-            map[CrowdAgentReposition::P_POSITION] = newPos;
-            map[CrowdAgentReposition::P_VELOCITY] = newVel;
-            map[CrowdAgentReposition::P_ARRIVED] = HasArrived();
-            SendEvent(E_CROWD_AGENT_REPOSITION, map);
+            using namespace CrowdAgentReposition;
+
+            VariantMap& map = GetEventDataMap();
+            map[P_NODE] = node_;
+            map[P_CROWD_AGENT] = this;
+            map[P_POSITION] = newPos;
+            map[P_VELOCITY] = newVel;
+            map[P_ARRIVED] = HasArrived();
+            map[P_TIMESTEP] = dt;
+            crowdManager_->SendEvent(E_CROWD_AGENT_REPOSITION, map);
 
 
             if (updateNodePosition_)
             if (updateNodePosition_)
             {
             {
@@ -410,30 +536,32 @@ void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& ne
         }
         }
 
 
         // Send a notification event if we've reached the destination
         // Send a notification event if we've reached the destination
-        CrowdTargetState newTargetState = GetTargetState();
+        CrowdAgentTargetState newTargetState = GetTargetState();
         CrowdAgentState newAgentState = GetAgentState();
         CrowdAgentState newAgentState = GetAgentState();
         if (newAgentState != previousAgentState_ || newTargetState != previousTargetState_)
         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] = newVel;
-            SendEvent(E_CROWD_AGENT_STATE_CHANGED, map);
+            using namespace CrowdAgentStateChanged;
+
+            VariantMap& map = GetEventDataMap();
+            map[P_NODE] = node_;
+            map[P_CROWD_AGENT] = this;
+            map[P_CROWD_TARGET_STATE] = newTargetState;
+            map[P_CROWD_AGENT_STATE] = newAgentState;
+            map[P_POSITION] = newPos;
+            map[P_VELOCITY] = newVel;
+            crowdManager_->SendEvent(E_CROWD_AGENT_STATE_CHANGED, map);
 
 
             // Send a failure event if either state is a failed status
             // Send a failure event if either state is a failed status
-            if (newAgentState == CROWD_AGENT_INVALID || newTargetState == CROWD_AGENT_TARGET_FAILED)
+            if (newAgentState == CA_STATE_INVALID || newTargetState == CA_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] = newVel;
-                SendEvent(E_CROWD_AGENT_FAILURE, map);
+                VariantMap& map = GetEventDataMap();
+                map[P_NODE] = node_;
+                map[P_CROWD_AGENT] = this;
+                map[P_CROWD_TARGET_STATE] = newTargetState;
+                map[P_CROWD_AGENT_STATE] = newAgentState;
+                map[P_POSITION] = newPos;
+                map[P_VELOCITY] = newVel;
+                crowdManager_->SendEvent(E_CROWD_AGENT_FAILURE, map);
             }
             }
 
 
             // State may have been altered during the handling of the event
             // State may have been altered during the handling of the event
@@ -443,43 +571,23 @@ void CrowdAgent::OnCrowdAgentReposition(const Vector3& newPos, const Vector3& ne
     }
     }
 }
 }
 
 
-PODVector<unsigned char> CrowdAgent::GetAgentDataAttr() const
+void CrowdAgent::OnNodeSet(Node* node)
 {
 {
-    const dtCrowdAgent* agent = GetDetourCrowdAgent();
-    if (!agent)
-        return Variant::emptyBuffer;
-
-    // Reading it back in isn't this simple, see SetAgentDataAttr
-    VectorBuffer ret;
-    ret.Write(agent, sizeof(dtCrowdAgent));
-
-    return ret.GetBuffer();
+    if (node)
+        node->AddListener(this);
 }
 }
 
 
-void CrowdAgent::SetAgentDataAttr(const PODVector<unsigned char>& value)
+void CrowdAgent::OnSceneSet(Scene* scene)
 {
 {
-    if (value.Empty())
-        return;
-
-    dtCrowdAgent* agent = const_cast<dtCrowdAgent*>(GetDetourCrowdAgent());
-    if (!agent)
-        return;
-
-    MemoryBuffer buffer(value);
-
-    // 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;
+    if (scene)
+    {
+        if (scene == node_)
+            LOGERROR(GetTypeName() + " should not be created to the root scene node");
+        crowdManager_ = scene->GetOrCreateComponent<CrowdManager>();
+        AddAgentToCrowd();
+    }
+    else
+        RemoveAgentFromCrowd();
 }
 }
 
 
 void CrowdAgent::OnMarkedDirty(Node* node)
 void CrowdAgent::OnMarkedDirty(Node* node)
@@ -492,10 +600,15 @@ void CrowdAgent::OnMarkedDirty(Node* node)
             memcpy(agent->npos, node->GetWorldPosition().Data(), sizeof(float) * 3);
             memcpy(agent->npos, node->GetWorldPosition().Data(), sizeof(float) * 3);
 
 
             // If the node has been externally altered, provide the opportunity for DetourCrowd to reevaluate the crowd agent
             // If the node has been externally altered, provide the opportunity for DetourCrowd to reevaluate the crowd agent
-            if (agent->state == CROWD_AGENT_INVALID)
-                agent->state = CROWD_AGENT_READY;
+            if (agent->state == CA_STATE_INVALID)
+                agent->state = CA_STATE_WALKING;
         }
         }
     }
     }
 }
 }
 
 
+const dtCrowdAgent* CrowdAgent::GetDetourCrowdAgent() const
+{
+    return IsInCrowd() ? crowdManager_->GetDetourCrowdAgent(agentCrowdId_) : 0;
+}
+
 }
 }

+ 110 - 57
Source/Atomic/Navigation/CrowdAgent.h

@@ -22,35 +22,58 @@
 
 
 #pragma once
 #pragma once
 
 
+#include "../Navigation/CrowdManager.h"
 #include "../Scene/Component.h"
 #include "../Scene/Component.h"
-#include "../Navigation/DetourCrowdManager.h"
 
 
 namespace Atomic
 namespace Atomic
 {
 {
 
 
-enum CrowdTargetState
+enum CrowdAgentRequestedTarget
 {
 {
-    CROWD_AGENT_TARGET_NONE = 0,
-    CROWD_AGENT_TARGET_FAILED,
-    CROWD_AGENT_TARGET_VALID,
-    CROWD_AGENT_TARGET_REQUESTING,
-    CROWD_AGENT_TARGET_WAITINGFORQUEUE,
-    CROWD_AGENT_TARGET_WAITINGFORPATH,
-    CROWD_AGENT_TARGET_VELOCITY
+    CA_REQUESTEDTARGET_NONE = 0,
+    CA_REQUESTEDTARGET_POSITION,
+    CA_REQUESTEDTARGET_VELOCITY
+};
+
+enum CrowdAgentTargetState
+{
+    CA_TARGET_NONE = 0,
+    CA_TARGET_FAILED,
+    CA_TARGET_VALID,
+    CA_TARGET_REQUESTING,
+    CA_TARGET_WAITINGFORQUEUE,
+    CA_TARGET_WAITINGFORPATH,
+    CA_TARGET_VELOCITY
 };
 };
 
 
 enum CrowdAgentState
 enum CrowdAgentState
 {
 {
-    CROWD_AGENT_INVALID = 0,      ///< The agent is not in a valid state.
-    CROWD_AGENT_READY,            ///< The agent is traversing a normal navigation mesh polygon.
-    CROWD_AGENT_TRAVERSINGLINK    ///< The agent is traversing an off-mesh connection.
+    CA_STATE_INVALID = 0,   ///< The agent is not in a valid state.
+    CA_STATE_WALKING,       ///< The agent is traversing a normal navigation mesh polygon.
+    CA_STATE_OFFMESH        ///< The agent is traversing an off-mesh connection.
+};
+
+enum NavigationQuality
+{
+    NAVIGATIONQUALITY_LOW = 0,
+    NAVIGATIONQUALITY_MEDIUM = 1,
+    NAVIGATIONQUALITY_HIGH = 2
+};
+
+enum NavigationPushiness
+{
+    NAVIGATIONPUSHINESS_LOW = 0,
+    NAVIGATIONPUSHINESS_MEDIUM,
+    NAVIGATIONPUSHINESS_HIGH
 };
 };
 
 
-/// DetourCrowd Agent, requires a DetourCrowdManager in the scene. Agent's radius and height is set through the navigation mesh.
+/// Crowd agent component, requires a CrowdManager component in the scene. When not set explicitly, agent's radius and height are defaulted to navigation mesh's agent radius and height, respectively.
 class ATOMIC_API CrowdAgent : public Component
 class ATOMIC_API CrowdAgent : public Component
 {
 {
     OBJECT(CrowdAgent);
     OBJECT(CrowdAgent);
-    friend class DetourCrowdManager;
+
+    friend class CrowdManager;
+    friend void CrowdAgentUpdateCallback(dtCrowdAgent* ag, float dt);
 
 
 public:
 public:
     /// Construct.
     /// Construct.
@@ -59,6 +82,8 @@ public:
     virtual ~CrowdAgent();
     virtual ~CrowdAgent();
     /// Register object factory.
     /// Register object factory.
     static void RegisterObject(Context* context);
     static void RegisterObject(Context* context);
+    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
+    virtual void ApplyAttributes();
 
 
     /// Handle enabled/disabled state change.
     /// Handle enabled/disabled state change.
     virtual void OnSetEnabled();
     virtual void OnSetEnabled();
@@ -67,70 +92,93 @@ public:
     /// Draw debug feelers.
     /// Draw debug feelers.
     virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
     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.
-    void SetMoveTarget(const Vector3& position);
-    /// Reset any request for the specified agent.
-    void ResetMoveTarget();
-    /// Submit a new move velocity request for this agent.
-    void SetMoveVelocity(const Vector3& velocity);
+    /// Submit a new target position request for this agent.
+    void SetTargetPosition(const Vector3& position);
+    /// Submit a new target velocity request for this agent.
+    void SetTargetVelocity(const Vector3& velocity);
+    /// Reset any target request for the specified agent.
+    void ResetTarget();
     /// Update the node position. When set to false, the node position should be updated by other means (e.g. using Physics) in response to the E_CROWD_AGENT_REPOSITION event.
     /// Update the node position. When set to false, the node position should be updated by other means (e.g. using Physics) in response to the E_CROWD_AGENT_REPOSITION event.
     void SetUpdateNodePosition(bool unodepos);
     void SetUpdateNodePosition(bool unodepos);
     /// Set the agent's max acceleration.
     /// Set the agent's max acceleration.
-    void SetMaxAccel(float val);
+    void SetMaxAccel(float maxAccel);
     /// Set the agent's max velocity.
     /// Set the agent's max velocity.
-    void SetMaxSpeed(float val);
+    void SetMaxSpeed(float maxSpeed);
     /// Set the agent's radius.
     /// Set the agent's radius.
-    void SetRadius(float val);
+    void SetRadius(float radius);
     /// Set the agent's height.
     /// Set the agent's height.
-    void SetHeight(float val);
+    void SetHeight(float height);
+    /// Set the agent's query filter type.
+    void SetQueryFilterType(unsigned queryFilterType);
+    /// Set the agent's obstacle avoidance type.
+    void SetObstacleAvoidanceType(unsigned obstacleAvoidanceType);
     /// Set the agent's navigation quality.
     /// Set the agent's navigation quality.
     void SetNavigationQuality(NavigationQuality val);
     void SetNavigationQuality(NavigationQuality val);
     /// Set the agent's navigation pushiness.
     /// Set the agent's navigation pushiness.
     void SetNavigationPushiness(NavigationPushiness val);
     void SetNavigationPushiness(NavigationPushiness val);
 
 
-    /// Get the navigation filter type this agent is using.
-    unsigned GetNavigationFilterType() const { return filterType_; }
     /// Return the agent's position.
     /// Return the agent's position.
     Vector3 GetPosition() const;
     Vector3 GetPosition() const;
     /// Return the agent's desired velocity.
     /// Return the agent's desired velocity.
     Vector3 GetDesiredVelocity() const;
     Vector3 GetDesiredVelocity() const;
     /// Return the agent's actual velocity.
     /// Return the agent's actual velocity.
     Vector3 GetActualVelocity() const;
     Vector3 GetActualVelocity() const;
-    /// Return the agent's target position.
+
+    /// Return the agent's requested target position.
     const Vector3& GetTargetPosition() const { return targetPosition_; }
     const Vector3& GetTargetPosition() const { return targetPosition_; }
+
+    /// Return the agent's requested target velocity.
+    const Vector3& GetTargetVelocity() const { return targetVelocity_; }
+
+    /// Return the agent's requested target type, if any.
+    CrowdAgentRequestedTarget GetRequestedTargetType() const { return requestedTargetType_; }
+
     /// Return the agent's  state.
     /// Return the agent's  state.
     CrowdAgentState GetAgentState() const;
     CrowdAgentState GetAgentState() const;
     /// Return the agent's target state.
     /// Return the agent's target state.
-    CrowdTargetState GetTargetState() const;
+    CrowdAgentTargetState GetTargetState() const;
+
     /// Return true when the node's position should be updated by the CrowdManager.
     /// Return true when the node's position should be updated by the CrowdManager.
     bool GetUpdateNodePosition() const { return updateNodePosition_; }
     bool GetUpdateNodePosition() const { return updateNodePosition_; }
+
     /// Return the agent id.
     /// Return the agent id.
     int GetAgentCrowdId() const { return agentCrowdId_; }
     int GetAgentCrowdId() const { return agentCrowdId_; }
-    /// Get the agent's max velocity.
-    float GetMaxSpeed() const { return maxSpeed_; }
+
     /// Get the agent's max acceleration.
     /// Get the agent's max acceleration.
     float GetMaxAccel() const { return maxAccel_; }
     float GetMaxAccel() const { return maxAccel_; }
+
+    /// Get the agent's max velocity.
+    float GetMaxSpeed() const { return maxSpeed_; }
+
     /// Get the agent's radius.
     /// Get the agent's radius.
     float GetRadius() const { return radius_; }
     float GetRadius() const { return radius_; }
+
     /// Get the agent's height.
     /// Get the agent's height.
     float GetHeight() const { return height_; }
     float GetHeight() const { return height_; }
+
+    /// Get the agent's query filter type.
+    unsigned GetQueryFilterType() const { return queryFilterType_; }
+
+    /// Get the agent's obstacle avoidance type.
+    unsigned GetObstacleAvoidanceType() const { return obstacleAvoidanceType_; }
+
     /// Get the agent's navigation quality.
     /// Get the agent's navigation quality.
-    NavigationQuality GetNavigationQuality() const {return navQuality_; }
+    NavigationQuality GetNavigationQuality() const { return navQuality_; }
+
     /// Get the agent's navigation pushiness.
     /// Get the agent's navigation pushiness.
-    NavigationPushiness GetNavigationPushiness() const {return navPushiness_; }
+    NavigationPushiness GetNavigationPushiness() const { return navPushiness_; }
+
+    /// Return true when the agent has a target.
+    bool HasRequestedTarget() const { return requestedTargetType_ != CA_REQUESTEDTARGET_NONE; }
+
     /// Return true when the agent has arrived at its target.
     /// Return true when the agent has arrived at its target.
     bool HasArrived() const;
     bool HasArrived() const;
-
-    /// Get serialized data of internal state.
-    PODVector<unsigned char> GetAgentDataAttr() const;
-    /// Set serialized data of internal state.
-    void SetAgentDataAttr(const PODVector<unsigned char>& value);
+    /// Return true when the agent is in crowd (being managed by a crowd manager).
+    bool IsInCrowd() const;
 
 
 protected:
 protected:
-    /// Update the nodes position if updateNodePosition is set. Is called in DetourCrowdManager::Update().
-    virtual void OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newVel);
+    /// Handle crowd agent being updated. It is called by CrowdManager::Update() via callback.
+    virtual void OnCrowdUpdate(dtCrowdAgent* ag, float dt);
     /// Handle node being assigned.
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
     virtual void OnNodeSet(Node* node);
     /// Handle node being assigned.
     /// Handle node being assigned.
@@ -139,21 +187,24 @@ protected:
     virtual void OnMarkedDirty(Node* node);
     virtual void OnMarkedDirty(Node* node);
     /// Get internal Detour crowd agent.
     /// Get internal Detour crowd agent.
     const dtCrowdAgent* GetDetourCrowdAgent() const;
     const dtCrowdAgent* GetDetourCrowdAgent() const;
+
 private:
 private:
-    /// Create or re-add.
-    void AddAgentToCrowd();
-    /// Remove.
+    /// Update Detour crowd agent parameter.
+    void UpdateParameters(unsigned scope = M_MAX_UNSIGNED);
+    /// Add agent into crowd.
+    int AddAgentToCrowd(bool force = false);
+    /// Remove agent from crowd.
     void RemoveAgentFromCrowd();
     void RemoveAgentFromCrowd();
-    /// Detour crowd manager.
-    WeakPtr<DetourCrowdManager> crowdManager_;
-    /// Flag indicating agent is in DetourCrowd.
-    bool inCrowd_;
-    /// DetourCrowd reference to this agent.
+    /// Crowd manager.
+    WeakPtr<CrowdManager> crowdManager_;
+    /// Crowd manager reference to this agent.
     int agentCrowdId_;
     int agentCrowdId_;
-    /// Reference to poly closest to requested target position.
-    unsigned targetRef_;
-    /// Actual target position, closest to that requested.
+    /// Requested target position.
     Vector3 targetPosition_;
     Vector3 targetPosition_;
+    /// Requested target velocity.
+    Vector3 targetVelocity_;
+    /// Requested target type.
+    CrowdAgentRequestedTarget requestedTargetType_;
     /// Flag indicating the node's position should be updated by Detour crowd manager.
     /// Flag indicating the node's position should be updated by Detour crowd manager.
     bool updateNodePosition_;
     bool updateNodePosition_;
     /// Agent's max acceleration.
     /// Agent's max acceleration.
@@ -164,16 +215,18 @@ private:
     float radius_;
     float radius_;
     /// Agent's height, if 0 the navigation mesh's setting will be used.
     /// Agent's height, if 0 the navigation mesh's setting will be used.
     float height_;
     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.
+    /// Agent's query filter type, it is an index to the query filter buffer configured in Detour crowd manager.
+    unsigned queryFilterType_;
+    /// Agent's obstacle avoidance type, it is an index to the obstacle avoidance array configured in Detour crowd manager. It is ignored when agent's navigation quality is not set to "NAVIGATIONQUALITY_HIGH".
+    unsigned obstacleAvoidanceType_;
+    /// Agent's navigation quality. The higher the setting, the higher the CPU usage during crowd simulation.
     NavigationQuality navQuality_;
     NavigationQuality navQuality_;
-    /// Agent's Navigation Pushiness.
+    /// Agent's navigation pushiness. The higher the setting, the stronger the agent pushes its colliding neighbours around.
     NavigationPushiness navPushiness_;
     NavigationPushiness navPushiness_;
     /// Agent's previous position used to check for position changes.
     /// Agent's previous position used to check for position changes.
     Vector3 previousPosition_;
     Vector3 previousPosition_;
     /// Agent's previous target state used to check for state changes.
     /// Agent's previous target state used to check for state changes.
-    CrowdTargetState previousTargetState_;
+    CrowdAgentTargetState previousTargetState_;
     /// Agent's previous agent state used to check for state changes.
     /// Agent's previous agent state used to check for state changes.
     CrowdAgentState previousAgentState_;
     CrowdAgentState previousAgentState_;
     /// Internal flag to ignore transform changes because it came from us, used in OnCrowdAgentReposition().
     /// Internal flag to ignore transform changes because it came from us, used in OnCrowdAgentReposition().

+ 690 - 0
Source/Atomic/Navigation/CrowdManager.cpp

@@ -0,0 +1,690 @@
+//
+// 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 "../Precompiled.h"
+
+#include "../Core/Context.h"
+#include "../Core/Profiler.h"
+#include "../Graphics/DebugRenderer.h"
+#include "../IO/Log.h"
+#include "../Navigation/CrowdAgent.h"
+#include "../Navigation/CrowdManager.h"
+#include "../Navigation/DynamicNavigationMesh.h"
+#include "../Navigation/NavigationEvents.h"
+#include "../Scene/Node.h"
+#include "../Scene/Scene.h"
+#include "../Scene/SceneEvents.h"
+
+#include <DetourCrowd/include/DetourCrowd.h>
+
+#include "../DebugNew.h"
+
+namespace Atomic
+{
+
+extern const char* NAVIGATION_CATEGORY;
+
+static const unsigned DEFAULT_MAX_AGENTS = 512;
+static const float DEFAULT_MAX_AGENT_RADIUS = 0.f;
+
+void CrowdAgentUpdateCallback(dtCrowdAgent* ag, float dt)
+{
+    static_cast<CrowdAgent*>(ag->params.userData)->OnCrowdUpdate(ag, dt);
+}
+
+CrowdManager::CrowdManager(Context* context) :
+    Component(context),
+    crowd_(0),
+    navigationMesh_(0),
+    navigationMeshId_(0),
+    maxAgents_(DEFAULT_MAX_AGENTS),
+    maxAgentRadius_(DEFAULT_MAX_AGENT_RADIUS),
+    numQueryFilterTypes_(0),
+    numObstacleAvoidanceTypes_(0)
+{
+    // The actual buffer is allocated inside dtCrowd, we only track the number of "slots" being configured explicitly
+    numAreas_.Reserve(DT_CROWD_MAX_QUERY_FILTER_TYPE);
+    for (unsigned i = 0; i < DT_CROWD_MAX_QUERY_FILTER_TYPE; ++i)
+        numAreas_.Push(0);
+}
+
+CrowdManager::~CrowdManager()
+{
+    dtFreeCrowd(crowd_);
+    crowd_ = 0;
+}
+
+void CrowdManager::RegisterObject(Context* context)
+{
+    context->RegisterFactory<CrowdManager>(NAVIGATION_CATEGORY);
+
+    ATTRIBUTE("Max Agents", unsigned, maxAgents_, DEFAULT_MAX_AGENTS, AM_DEFAULT);
+    ATTRIBUTE("Max Agent Radius", float, maxAgentRadius_, DEFAULT_MAX_AGENT_RADIUS, AM_DEFAULT);
+    ATTRIBUTE("Navigation Mesh", unsigned, navigationMeshId_, 0, AM_DEFAULT | AM_COMPONENTID);
+    MIXED_ACCESSOR_ATTRIBUTE("Filter Types", GetQueryFilterTypesAttr, SetQueryFilterTypesAttr, VariantVector,
+        Variant::emptyVariantVector, AM_DEFAULT);
+    MIXED_ACCESSOR_ATTRIBUTE("Obstacle Avoidance Types", GetObstacleAvoidanceTypesAttr, SetObstacleAvoidanceTypesAttr,
+        VariantVector, Variant::emptyVariantVector, AM_DEFAULT);
+}
+
+void CrowdManager::ApplyAttributes()
+{
+    // Values from Editor, saved-file, or network must be checked before applying
+    maxAgents_ = (unsigned)Max(1, maxAgents_);
+    maxAgentRadius_ = Max(0.f, maxAgentRadius_);
+
+    bool navMeshChange = false;
+    Scene* scene = GetScene();
+    if (scene && navigationMeshId_)
+    {
+        NavigationMesh* navMesh = dynamic_cast<NavigationMesh*>(scene->GetComponent(navigationMeshId_));
+        if (navMesh)
+        {
+            navMeshChange = navMesh != navigationMesh_;
+            navigationMesh_ = navMesh;
+        }
+    }
+    // In case of receiving an invalid component id, revert it back to the existing navmesh component id (if any)
+    navigationMeshId_ = navigationMesh_ ? navigationMesh_->GetID() : 0;
+
+    // If the Detour crowd initialization parameters have changed then recreate it
+    if (crowd_ && (navMeshChange || crowd_->getAgentCount() != maxAgents_ || crowd_->getMaxAgentRadius() != maxAgentRadius_))
+        CreateCrowd();
+}
+
+void CrowdManager::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    if (debug && crowd_)
+    {
+        // Current position-to-target line
+        for (int i = 0; i < crowd_->getAgentCount(); i++)
+        {
+            const dtCrowdAgent* ag = crowd_->getAgent(i);
+            if (!ag->active)
+                continue;
+
+            // Draw CrowdAgent shape (from its radius & height)
+            CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(ag->params.userData);
+            crowdAgent->DrawDebugGeometry(debug, depthTest);
+
+            // Draw move target if any
+            if (crowdAgent->GetTargetState() == CA_TARGET_NONE)
+                continue;
+
+            Color color(0.6f, 0.2f, 0.2f, 1.0f);
+
+            // Draw 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);
+
+            // Draw target circle
+            debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
+        }
+    }
+}
+
+void CrowdManager::DrawDebugGeometry(bool depthTest)
+{
+    Scene* scene = GetScene();
+    if (scene)
+    {
+        DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
+        if (debug)
+            DrawDebugGeometry(debug, depthTest);
+    }
+}
+
+void CrowdManager::SetCrowdTarget(const Vector3& position, Node* node)
+{
+    if (!crowd_)
+        return;
+
+    PODVector<CrowdAgent*> agents = GetAgents(node, false);     // Get all crowd agent components
+    Vector3 moveTarget(position);
+    for (unsigned i = 0; i < agents.Size(); ++i)
+    {
+        // Give application a chance to determine the desired crowd formation when they reach the target position
+        CrowdAgent* agent = agents[i];
+
+        using namespace CrowdAgentFormation;
+
+        VariantMap& map = GetEventDataMap();
+        map[P_NODE] = agent->GetNode();
+        map[P_CROWD_AGENT] = agent;
+        map[P_INDEX] = i;
+        map[P_SIZE] = agents.Size();
+        map[P_POSITION] = moveTarget;   // Expect the event handler will modify this position accordingly
+
+        SendEvent(E_CROWD_AGENT_FORMATION, map);
+
+        moveTarget = map[P_POSITION].GetVector3();
+        agent->SetTargetPosition(moveTarget);
+    }
+}
+
+void CrowdManager::SetCrowdVelocity(const Vector3& velocity, Node* node)
+{
+    if (!crowd_)
+        return;
+
+    PODVector<CrowdAgent*> agents = GetAgents(node, true);      // Get only crowd agent components already in the crowd
+    for (unsigned i = 0; i < agents.Size(); ++i)
+        agents[i]->SetTargetVelocity(velocity);
+}
+
+void CrowdManager::ResetCrowdTarget(Node* node)
+{
+    if (!crowd_)
+        return;
+
+    PODVector<CrowdAgent*> agents = GetAgents(node, true);
+    for (unsigned i = 0; i < agents.Size(); ++i)
+        agents[i]->ResetTarget();
+}
+
+void CrowdManager::SetMaxAgents(unsigned maxAgents)
+{
+    if (maxAgents != maxAgents_ && maxAgents > 0)
+    {
+        maxAgents_ = maxAgents;
+        CreateCrowd();
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdManager::SetMaxAgentRadius(float maxAgentRadius)
+{
+    if (maxAgentRadius != maxAgentRadius_ && maxAgentRadius > 0.f)
+    {
+        maxAgentRadius_ = maxAgentRadius;
+        CreateCrowd();
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdManager::SetNavigationMesh(NavigationMesh* navMesh)
+{
+    if (navMesh != navigationMesh_)     // It is possible to reset navmesh pointer back to 0
+    {
+        navigationMesh_ = navMesh;
+        navigationMeshId_ = navMesh ? navMesh->GetID() : 0;
+        CreateCrowd();
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdManager::SetQueryFilterTypesAttr(const VariantVector& value)
+{
+    if (!crowd_)
+        return;
+
+    unsigned index = 0;
+    unsigned queryFilterType = 0;
+    numQueryFilterTypes_ = index < value.Size() ? Min(value[index++].GetUInt(), DT_CROWD_MAX_QUERY_FILTER_TYPE) : 0;
+
+    while (queryFilterType < numQueryFilterTypes_)
+    {
+        if (index + 3 <= value.Size())
+        {
+            dtQueryFilter* filter = crowd_->getEditableFilter(queryFilterType);
+            assert(filter);
+            filter->setIncludeFlags((unsigned short)value[index++].GetUInt());
+            filter->setExcludeFlags((unsigned short)value[index++].GetUInt());
+            unsigned prevNumAreas = numAreas_[queryFilterType];
+            numAreas_[queryFilterType] = Min(value[index++].GetUInt(), DT_MAX_AREAS);
+
+            // Must loop thru based on previous number of areas, the new area cost (if any) can only be set in the next attribute get/set iteration
+            if (index + prevNumAreas <= value.Size())
+            {
+                for (int i = 0; i < prevNumAreas; ++i)
+                    filter->setAreaCost(i, value[index++].GetFloat());
+            }
+        }
+        ++queryFilterType;
+    }
+}
+
+void CrowdManager::SetIncludeFlags(unsigned queryFilterType, unsigned short flags)
+{
+    dtQueryFilter* filter = const_cast<dtQueryFilter*>(GetDetourQueryFilter(queryFilterType));
+    if (filter)
+    {
+        filter->setIncludeFlags(flags);
+        if (numQueryFilterTypes_ < queryFilterType + 1)
+            numQueryFilterTypes_ = queryFilterType + 1;
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdManager::SetExcludeFlags(unsigned queryFilterType, unsigned short flags)
+{
+    dtQueryFilter* filter = const_cast<dtQueryFilter*>(GetDetourQueryFilter(queryFilterType));
+    if (filter)
+    {
+        filter->setExcludeFlags(flags);
+        if (numQueryFilterTypes_ < queryFilterType + 1)
+            numQueryFilterTypes_ = queryFilterType + 1;
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdManager::SetAreaCost(unsigned queryFilterType, unsigned areaID, float cost)
+{
+    dtQueryFilter* filter = const_cast<dtQueryFilter*>(GetDetourQueryFilter(queryFilterType));
+    if (filter && areaID < DT_MAX_AREAS)
+    {
+        filter->setAreaCost((int)areaID, cost);
+        if (numQueryFilterTypes_ < queryFilterType + 1)
+            numQueryFilterTypes_ = queryFilterType + 1;
+        if (numAreas_[queryFilterType] < areaID + 1)
+            numAreas_[queryFilterType] = areaID + 1;
+        MarkNetworkUpdate();
+    }
+}
+
+void CrowdManager::SetObstacleAvoidanceTypesAttr(const VariantVector& value)
+{
+    if (!crowd_)
+        return;
+
+    unsigned index = 0;
+    unsigned obstacleAvoidanceType = 0;
+    numObstacleAvoidanceTypes_ = index < value.Size() ? Min(value[index++].GetUInt(), DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS) : 0;
+
+    while (obstacleAvoidanceType < numObstacleAvoidanceTypes_)
+    {
+        if (index + 10 <= value.Size())
+        {
+            dtObstacleAvoidanceParams params;
+            params.velBias = value[index++].GetFloat();
+            params.weightDesVel = value[index++].GetFloat();
+            params.weightCurVel = value[index++].GetFloat();
+            params.weightSide = value[index++].GetFloat();
+            params.weightToi = value[index++].GetFloat();
+            params.horizTime = value[index++].GetFloat();
+            params.gridSize = (unsigned char)value[index++].GetUInt();
+            params.adaptiveDivs = (unsigned char)value[index++].GetUInt();
+            params.adaptiveRings = (unsigned char)value[index++].GetUInt();
+            params.adaptiveDepth = (unsigned char)value[index++].GetUInt();
+            crowd_->setObstacleAvoidanceParams(obstacleAvoidanceType, &params);
+        }
+        ++obstacleAvoidanceType;
+    }
+}
+
+void CrowdManager::SetObstacleAvoidanceParams(unsigned obstacleAvoidanceType, const CrowdObstacleAvoidanceParams& params)
+{
+    if (crowd_ && obstacleAvoidanceType < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS)
+    {
+        crowd_->setObstacleAvoidanceParams(obstacleAvoidanceType, reinterpret_cast<const dtObstacleAvoidanceParams*>(&params));
+        if (numObstacleAvoidanceTypes_ < obstacleAvoidanceType + 1)
+            numObstacleAvoidanceTypes_ = obstacleAvoidanceType + 1;
+        MarkNetworkUpdate();
+    }
+}
+
+Vector3 CrowdManager::FindNearestPoint(const Vector3& point, int queryFilterType, dtPolyRef* nearestRef)
+{
+    if (nearestRef)
+        *nearestRef = 0;
+    return crowd_ && navigationMesh_ ?
+        navigationMesh_->FindNearestPoint(point, crowd_->getQueryExtents(), crowd_->getFilter(queryFilterType), nearestRef) : point;
+}
+
+Vector3 CrowdManager::MoveAlongSurface(const Vector3& start, const Vector3& end, int queryFilterType, int maxVisited)
+{
+    return crowd_ && navigationMesh_ ?
+        navigationMesh_->MoveAlongSurface(start, end, crowd_->getQueryExtents(), maxVisited, crowd_->getFilter(queryFilterType)) :
+        end;
+}
+
+void CrowdManager::FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, int queryFilterType)
+{
+    if (crowd_ && navigationMesh_)
+        navigationMesh_->FindPath(dest, start, end, crowd_->getQueryExtents(), crowd_->getFilter(queryFilterType));
+}
+
+Vector3 CrowdManager::GetRandomPoint(int queryFilterType, dtPolyRef* randomRef)
+{
+    if (randomRef)
+        *randomRef = 0;
+    return crowd_ && navigationMesh_ ? navigationMesh_->GetRandomPoint(crowd_->getFilter(queryFilterType), randomRef) :
+        Vector3::ZERO;
+}
+
+Vector3 CrowdManager::GetRandomPointInCircle(const Vector3& center, float radius, int queryFilterType, dtPolyRef* randomRef)
+{
+    if (randomRef)
+        *randomRef = 0;
+    return crowd_ && navigationMesh_ ?
+        navigationMesh_->GetRandomPointInCircle(center, radius, crowd_->getQueryExtents(), crowd_->getFilter(queryFilterType),
+            randomRef) : center;
+}
+
+float CrowdManager::GetDistanceToWall(const Vector3& point, float radius, int queryFilterType, Vector3* hitPos, Vector3* hitNormal)
+{
+    if (hitPos)
+        *hitPos = Vector3::ZERO;
+    if (hitNormal)
+        *hitNormal = Vector3::DOWN;
+    return crowd_ && navigationMesh_ ?
+        navigationMesh_->GetDistanceToWall(point, radius, crowd_->getQueryExtents(), crowd_->getFilter(queryFilterType), hitPos,
+            hitNormal) : radius;
+}
+
+Vector3 CrowdManager::Raycast(const Vector3& start, const Vector3& end, int queryFilterType, Vector3* hitNormal)
+{
+    if (hitNormal)
+        *hitNormal = Vector3::DOWN;
+    return crowd_ && navigationMesh_ ?
+        navigationMesh_->Raycast(start, end, crowd_->getQueryExtents(), crowd_->getFilter(queryFilterType), hitNormal) : end;
+}
+
+unsigned CrowdManager::GetNumAreas(unsigned queryFilterType) const
+{
+    return queryFilterType < numQueryFilterTypes_ ? numAreas_[queryFilterType] : 0;
+}
+
+VariantVector CrowdManager::GetQueryFilterTypesAttr() const
+{
+    VariantVector ret;
+    if (crowd_)
+    {
+        unsigned totalNumAreas = 0;
+        for (unsigned i = 0; i < numQueryFilterTypes_; ++i)
+            totalNumAreas += numAreas_[i];
+
+        ret.Reserve(numQueryFilterTypes_ * 3 + totalNumAreas + 1);
+        ret.Push(numQueryFilterTypes_);
+
+        for (unsigned i = 0; i < numQueryFilterTypes_; ++i)
+        {
+            const dtQueryFilter* filter = crowd_->getFilter(i);
+            assert(filter);
+            ret.Push(filter->getIncludeFlags());
+            ret.Push(filter->getExcludeFlags());
+            ret.Push(numAreas_[i]);
+
+            for (unsigned j = 0; j < numAreas_[i]; ++j)
+                ret.Push(filter->getAreaCost(j));
+        }
+    }
+    else
+        ret.Push(0);
+
+    return ret;
+}
+
+unsigned short CrowdManager::GetIncludeFlags(unsigned queryFilterType) const
+{
+    if (queryFilterType >= numQueryFilterTypes_)
+        LOGWARNINGF("Query filter type %d is not configured yet, returning the default include flags initialized by dtCrowd",
+            queryFilterType);
+    const dtQueryFilter* filter = GetDetourQueryFilter(queryFilterType);
+    return (unsigned short)(filter ? filter->getIncludeFlags() : 0xffff);
+}
+
+unsigned short CrowdManager::GetExcludeFlags(unsigned queryFilterType) const
+{
+    if (queryFilterType >= numQueryFilterTypes_)
+        LOGWARNINGF("Query filter type %d is not configured yet, returning the default exclude flags initialized by dtCrowd",
+            queryFilterType);
+    const dtQueryFilter* filter = GetDetourQueryFilter(queryFilterType);
+    return (unsigned short)(filter ? filter->getExcludeFlags() : 0);
+}
+
+float CrowdManager::GetAreaCost(unsigned queryFilterType, unsigned areaID) const
+{
+    if (queryFilterType >= numQueryFilterTypes_ || areaID >= numAreas_[queryFilterType])
+        LOGWARNINGF(
+            "Query filter type %d and/or area id %d are not configured yet, returning the default area cost initialized by dtCrowd",
+            queryFilterType, areaID);
+    const dtQueryFilter* filter = GetDetourQueryFilter(queryFilterType);
+    return filter ? filter->getAreaCost((int)areaID) : 1.f;
+}
+
+VariantVector CrowdManager::GetObstacleAvoidanceTypesAttr() const
+{
+    VariantVector ret;
+    if (crowd_)
+    {
+        ret.Reserve(numObstacleAvoidanceTypes_ * 10 + 1);
+        ret.Push(numObstacleAvoidanceTypes_);
+
+        for (unsigned i = 0; i < numObstacleAvoidanceTypes_; ++i)
+        {
+            const dtObstacleAvoidanceParams* params = crowd_->getObstacleAvoidanceParams(i);
+            assert(params);
+            ret.Push(params->velBias);
+            ret.Push(params->weightDesVel);
+            ret.Push(params->weightCurVel);
+            ret.Push(params->weightSide);
+            ret.Push(params->weightToi);
+            ret.Push(params->horizTime);
+            ret.Push(params->gridSize);
+            ret.Push(params->adaptiveDivs);
+            ret.Push(params->adaptiveRings);
+            ret.Push(params->adaptiveDepth);
+        }
+    }
+    else
+        ret.Push(0);
+
+    return ret;
+}
+
+const CrowdObstacleAvoidanceParams& CrowdManager::GetObstacleAvoidanceParams(unsigned obstacleAvoidanceType) const
+{
+    static const CrowdObstacleAvoidanceParams EMPTY_PARAMS = CrowdObstacleAvoidanceParams();
+    const dtObstacleAvoidanceParams* params = crowd_ ? crowd_->getObstacleAvoidanceParams(obstacleAvoidanceType) : 0;
+    return params ? *reinterpret_cast<const CrowdObstacleAvoidanceParams*>(params) : EMPTY_PARAMS;
+}
+
+PODVector<CrowdAgent*> CrowdManager::GetAgents(Node* node, bool inCrowdFilter) const
+{
+    if (!node)
+        node = GetScene();
+    PODVector<CrowdAgent*> agents;
+    node->GetComponents<CrowdAgent>(agents, true);
+    if (inCrowdFilter)
+    {
+        PODVector<CrowdAgent*>::Iterator i = agents.Begin();
+        while (i != agents.End())
+        {
+            if ((*i)->IsInCrowd())
+                ++i;
+            else
+                i = agents.Erase(i);
+        }
+    }
+    return agents;
+}
+
+bool CrowdManager::CreateCrowd()
+{
+    if (!navigationMesh_ || !navigationMesh_->InitializeQuery())
+        return false;
+
+    // Preserve the existing crowd configuration before recreating it
+    VariantVector queryFilterTypeConfiguration, obstacleAvoidanceTypeConfiguration;
+    bool recreate = crowd_ != 0;
+    if (recreate)
+    {
+        queryFilterTypeConfiguration = GetQueryFilterTypesAttr();
+        obstacleAvoidanceTypeConfiguration = GetObstacleAvoidanceTypesAttr();
+        dtFreeCrowd(crowd_);
+    }
+    crowd_ = dtAllocCrowd();
+
+    // Initialize the crowd
+    if (maxAgentRadius_ == 0.f)
+        maxAgentRadius_ = navigationMesh_->GetAgentRadius();
+    if (!crowd_->init(maxAgents_, maxAgentRadius_, navigationMesh_->navMesh_, CrowdAgentUpdateCallback))
+    {
+        LOGERROR("Could not initialize DetourCrowd");
+        return false;
+    }
+
+    if (recreate)
+    {
+        // Reconfigure the newly initialized crowd
+        SetQueryFilterTypesAttr(queryFilterTypeConfiguration);
+        SetObstacleAvoidanceTypesAttr(obstacleAvoidanceTypeConfiguration);
+
+        // Re-add the existing crowd agents
+        PODVector<CrowdAgent*> agents = GetAgents();
+        for (unsigned i = 0; i < agents.Size(); ++i)
+        {
+            // Keep adding until the crowd cannot take it anymore
+            if (agents[i]->AddAgentToCrowd(true) == -1)
+            {
+                LOGWARNINGF("CrowdManager: %d crowd agents orphaned", agents.Size() - i);
+                break;
+            }
+        }
+    }
+
+    return true;
+}
+
+int CrowdManager::AddAgent(CrowdAgent* agent, const Vector3& pos)
+{
+    if (!crowd_ || !navigationMesh_ || !agent)
+        return -1;
+    dtCrowdAgentParams params;
+    params.userData = agent;
+    if (agent->radius_ == 0.f)
+        agent->radius_ = navigationMesh_->GetAgentRadius();
+    if (agent->height_ == 0.f)
+        agent->height_ = navigationMesh_->GetAgentHeight();
+    return crowd_->addAgent(pos.Data(), &params);
+}
+
+void CrowdManager::RemoveAgent(CrowdAgent* agent)
+{
+    if (!crowd_ || !agent)
+        return;
+    dtCrowdAgent* agt = crowd_->getEditableAgent(agent->GetAgentCrowdId());
+    if (agt)
+        agt->params.userData = 0;
+    crowd_->removeAgent(agent->GetAgentCrowdId());
+}
+
+void CrowdManager::OnSceneSet(Scene* scene)
+{
+    // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference
+    // to the scene's NavigationMesh
+    if (scene)
+    {
+        if (scene != node_)
+        {
+            LOGERROR("CrowdManager is a scene component and should only be attached to the scene node");
+            return;
+        }
+
+        SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, HANDLER(CrowdManager, HandleSceneSubsystemUpdate));
+
+        // Attempt to auto discover a NavigationMesh component (or its derivative) under the scene node
+        NavigationMesh* navMesh = scene->GetDerivedComponent<NavigationMesh>(true);
+        if (navMesh)
+        {
+            navigationMesh_ = navMesh;
+            navigationMeshId_ = navMesh->GetID();
+            CreateCrowd();
+
+            SubscribeToEvent(navMesh->GetNode(), E_NAVIGATION_MESH_REBUILT, HANDLER(CrowdManager, HandleNavMeshChanged));
+            SubscribeToEvent(navMesh->GetNode(), E_COMPONENTREMOVED, HANDLER(CrowdManager, HandleNavMeshChanged));
+        }
+    }
+    else
+    {
+        UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
+        UnsubscribeFromEvent(E_NAVIGATION_MESH_REBUILT);
+        UnsubscribeFromEvent(E_COMPONENTREMOVED);
+
+        navigationMesh_ = 0;
+    }
+}
+
+void CrowdManager::Update(float delta)
+{
+    assert(crowd_ && navigationMesh_);
+    PROFILE(UpdateCrowd);
+    crowd_->update(delta, 0);
+}
+
+const dtCrowdAgent* CrowdManager::GetDetourCrowdAgent(int agent) const
+{
+    return crowd_ ? crowd_->getAgent(agent) : 0;
+}
+
+const dtQueryFilter* CrowdManager::GetDetourQueryFilter(unsigned queryFilterType) const
+{
+    return crowd_ ? crowd_->getFilter(queryFilterType) : 0;
+}
+
+void CrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
+{
+    // Perform update tick as long as the crowd is initialized and the associated navmesh has not been removed
+    if (crowd_ && navigationMesh_)
+    {
+        using namespace SceneSubsystemUpdate;
+
+        if (IsEnabledEffective())
+            Update(eventData[P_TIMESTEP].GetFloat());
+    }
+}
+
+void CrowdManager::HandleNavMeshChanged(StringHash eventType, VariantMap& eventData)
+{
+    NavigationMesh* navMesh;
+    if (eventType == E_NAVIGATION_MESH_REBUILT)
+    {
+        // The mesh being rebuilt may not have existed before
+        navMesh = static_cast<NavigationMesh*>(eventData[NavigationMeshRebuilt::P_MESH].GetPtr());
+    }
+    else
+    {
+        // eventType == E_COMPONENTREMOVED
+        navMesh = static_cast<NavigationMesh*>(eventData[ComponentRemoved::P_COMPONENT].GetPtr());
+        // Only interested in navmesh component being used to initialized the crowd
+        if (navMesh != navigationMesh_)
+            return;
+        // Since this is a component removed event, reset our own navmesh pointer
+        navMesh = 0;
+    }
+    SetNavigationMesh(navMesh);
+}
+
+}

+ 198 - 0
Source/Atomic/Navigation/CrowdManager.h

@@ -0,0 +1,198 @@
+//
+// 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"
+
+#ifdef DT_POLYREF64
+typedef uint64_t dtPolyRef;
+#else
+typedef unsigned int dtPolyRef;
+#endif
+
+class dtCrowd;
+class dtQueryFilter;
+struct dtCrowdAgent;
+
+namespace Atomic
+{
+
+class CrowdAgent;
+class NavigationMesh;
+
+/// Parameter structure for obstacle avoidance params (copied from DetourObstacleAvoidance.h in order to hide Detour header from Atomic library users).
+struct CrowdObstacleAvoidanceParams
+{
+    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
+};
+
+/// Crowd manager scene component. Should be added only to the root scene node.
+class ATOMIC_API CrowdManager : public Component
+{
+    OBJECT(CrowdManager);
+
+    friend class CrowdAgent;
+
+public:
+    /// Construct.
+    CrowdManager(Context* context);
+    /// Destruct.
+    virtual ~CrowdManager();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
+    virtual void ApplyAttributes();
+
+    /// Draw the agents' pathing debug data.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+    /// Add debug geometry to the debug renderer.
+    void DrawDebugGeometry(bool depthTest);
+
+    /// Set the crowd target position. The target position is set to all crowd agents found in the specified node. Defaulted to scene node.
+    void SetCrowdTarget(const Vector3& position, Node* node = 0);
+    /// Set the crowd move velocity. The move velocity is applied to all crowd agents found in the specified node. Defaulted to scene node.
+    void SetCrowdVelocity(const Vector3& velocity, Node* node = 0);
+    /// Reset any crowd target for all crowd agents found in the specified node. Defaulted to scene node.
+    void ResetCrowdTarget(Node* node = 0);
+    /// Set the maximum number of agents.
+    void SetMaxAgents(unsigned maxAgents);
+    /// Set the maximum radius of any agent.
+    void SetMaxAgentRadius(float maxAgentRadius);
+    /// Assigns the navigation mesh for the crowd.
+    void SetNavigationMesh(NavigationMesh* navMesh);
+    /// Set all the query filter types configured in the crowd based on the corresponding attribute.
+    void SetQueryFilterTypesAttr(const VariantVector& value);
+    /// Set the include flags for the specified query filter type.
+    void SetIncludeFlags(unsigned queryFilterType, unsigned short flags);
+    /// Set the exclude flags for the specified query filter type.
+    void SetExcludeFlags(unsigned queryFilterType, unsigned short flags);
+    /// Set the cost of an area for the specified query filter type.
+    void SetAreaCost(unsigned queryFilterType, unsigned areaID, float cost);
+    /// Set all the obstacle avoidance types configured in the crowd based on the corresponding attribute.
+    void SetObstacleAvoidanceTypesAttr(const VariantVector& value);
+    /// Set the params for the specified obstacle avoidance type.
+    void SetObstacleAvoidanceParams(unsigned obstacleAvoidanceType, const CrowdObstacleAvoidanceParams& params);
+
+    /// Get all the crowd agent components in the specified node hierarchy. If the node is not specified then use scene node. When inCrowdFilter is set to true then only get agents that are in the crowd.
+    PODVector<CrowdAgent*> GetAgents(Node* node = 0, bool inCrowdFilter = true) const;
+    /// Find the nearest point on the navigation mesh to a given point using the crowd initialized query extent (based on maxAgentRadius) and the specified query filter type.
+    Vector3 FindNearestPoint(const Vector3& point, int queryFilterType, dtPolyRef* nearestRef = 0);
+    /// Try to move along the surface from one point to another using the crowd initialized query extent (based on maxAgentRadius) and the specified query filter type.
+    Vector3 MoveAlongSurface(const Vector3& start, const Vector3& end, int queryFilterType, int maxVisited = 3);
+    /// Find a path between world space points using the crowd initialized query extent (based on maxAgentRadius) and the specified query filter type. Return non-empty list of points if successful.
+    void FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, int queryFilterType);
+    /// Return a random point on the navigation mesh using the crowd initialized query extent (based on maxAgentRadius) and the specified query filter type.
+    Vector3 GetRandomPoint(int queryFilterType, dtPolyRef* randomRef = 0);
+    /// Return a random point on the navigation mesh within a circle using the crowd initialized query extent (based on maxAgentRadius) and the specified query filter type. The circle radius is only a guideline and in practice the returned point may be further away.
+    Vector3 GetRandomPointInCircle(const Vector3& center, float radius, int queryFilterType, dtPolyRef* randomRef = 0);
+    /// Return distance to wall from a point using the crowd initialized query extent (based on maxAgentRadius) and the specified query filter type. Maximum search radius must be specified.
+    float GetDistanceToWall(const Vector3& point, float radius, int queryFilterType, Vector3* hitPos = 0, Vector3* hitNormal = 0);
+    /// Perform a walkability raycast on the navigation mesh between start and end using the crowd initialized query extent (based on maxAgentRadius) and the specified query filter type. Return the point where a wall was hit, or the end point if no walls.
+    Vector3 Raycast(const Vector3& start, const Vector3& end, int queryFilterType, Vector3* hitNormal = 0);
+
+    /// Get the maximum number of agents.
+    unsigned GetMaxAgents() const { return maxAgents_; }
+
+    /// Get the maximum radius of any agent.
+    float GetMaxAgentRadius() const { return maxAgentRadius_; }
+
+    /// Get the Navigation mesh assigned to the crowd.
+    NavigationMesh* GetNavigationMesh() const { return navigationMesh_; }
+
+    /// Get the number of configured query filter types.
+    unsigned GetNumQueryFilterTypes() const { return numQueryFilterTypes_; }
+
+    /// Get the number of configured area in the specified query filter type.
+    unsigned GetNumAreas(unsigned queryFilterType) const;
+    /// Return all the filter types configured in the crowd as attribute.
+    VariantVector GetQueryFilterTypesAttr() const;
+    /// Get the include flags for the specified query filter type.
+    unsigned short GetIncludeFlags(unsigned queryFilterType) const;
+    /// Get the exclude flags for the specified query filter type.
+    unsigned short GetExcludeFlags(unsigned queryFilterType) const;
+    /// Get the cost of an area for the specified query filter type.
+    float GetAreaCost(unsigned queryFilterType, unsigned areaID) const;
+
+    /// Get the number of configured obstacle avoidance types.
+    unsigned GetNumObstacleAvoidanceTypes() const { return numObstacleAvoidanceTypes_; }
+
+    /// Return all the obstacle avoidance types configured in the crowd as attribute.
+    VariantVector GetObstacleAvoidanceTypesAttr() const;
+    /// Get the params for the specified obstacle avoidance type.
+    const CrowdObstacleAvoidanceParams& GetObstacleAvoidanceParams(unsigned obstacleAvoidanceType) const;
+
+protected:
+    /// Create and initialized internal Detour crowd object. When it is a recreate, it preserves the configuration and attempts to re-add existing agents in the previous crowd back to the newly created crowd.
+    bool CreateCrowd();
+    /// 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);
+
+protected:
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
+    /// Update the crowd simulation.
+    void Update(float delta);
+    /// Get the detour crowd agent.
+    const dtCrowdAgent* GetDetourCrowdAgent(int agent) const;
+    /// Get the detour query filter.
+    const dtQueryFilter* GetDetourQueryFilter(unsigned queryFilterType) const;
+
+    /// Get the internal detour crowd component.
+    dtCrowd* GetCrowd() const { return crowd_; }
+
+private:
+    /// Handle the scene subsystem update event.
+    void HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData);
+    /// Handle navigation mesh changed event. It can be navmesh being rebuilt or being removed from its node.
+    void HandleNavMeshChanged(StringHash eventType, VariantMap& eventData);
+
+    /// Internal Detour crowd object.
+    dtCrowd* crowd_;
+    /// NavigationMesh for which the crowd was created.
+    NavigationMesh* navigationMesh_;
+    /// The NavigationMesh component Id for pending crowd creation.
+    unsigned navigationMeshId_;
+    /// The maximum number of agents the crowd can manage.
+    unsigned maxAgents_;
+    /// The maximum radius of any agent that will be added to the crowd.
+    float maxAgentRadius_;
+    /// Number of query filter types configured in the crowd. Limit to DT_CROWD_MAX_QUERY_FILTER_TYPE.
+    unsigned numQueryFilterTypes_;
+    /// Number of configured area in each filter type. Limit to DT_MAX_AREAS.
+    PODVector<unsigned> numAreas_;
+    /// Number of obstacle avoidance types configured in the crowd. Limit to DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS.
+    unsigned numObstacleAvoidanceTypes_;
+};
+
+}

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

@@ -1,546 +0,0 @@
-//
-// 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 "../Precompiled.h"
-
-#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 ATOMIC_PHYSICS
-#include "../Physics/PhysicsEvents.h"
-#endif
-
-#include <DetourCrowd/include/DetourCrowd.h>
-#include <Recast/include/Recast.h>
-
-#include "../DebugNew.h"
-
-namespace Atomic
-{
-
-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_ = 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();
-}
-
-void DetourCrowdManager::SetCrowdTarget(const Vector3& position, int startId, int endId)
-{
-    startId = Max(0, startId);
-    endId = Clamp(endId, startId, agents_.Size() - 1);
-    Vector3 moveTarget(position);
-    for (int i = startId; i <= endId; ++i)
-    {
-        // Skip agent that does not have acceleration
-        if (agents_[i]->GetMaxAccel() > 0.f)
-        {
-            agents_[i]->SetMoveTarget(moveTarget);
-            // FIXME: Should reimplement this using event callback, i.e. it should be application-specific to decide what is the desired crowd formation when they reach the target
-            if (navigationMesh_)
-                moveTarget = navigationMesh_->FindNearestPoint(position + Vector3(Random(-4.5f, 4.5f), 0.0f, Random(-4.5f, 4.5f)), Vector3(1.0f, 1.0f, 1.0f));
-        }
-    }
-}
-
-void DetourCrowdManager::ResetCrowdTarget(int startId, int endId)
-{
-    startId = Max(0, startId);
-    endId = Clamp(endId, startId, agents_.Size() - 1);
-    for (int i = startId; i <= endId; ++i)
-    {
-        if (agents_[i]->GetMaxAccel() > 0.f)
-            agents_[i]->ResetMoveTarget();
-    }
-}
-
-void DetourCrowdManager::SetCrowdVelocity(const Vector3& velocity, int startId, int endId)
-{
-    startId = Max(0, startId);
-    endId = Clamp(endId, startId, agents_.Size() - 1);
-    for (int i = startId; i <= endId; ++i)
-    {
-        if (agents_[i]->GetMaxAccel() > 0.f)
-            agents_[i]->SetMoveVelocity(velocity);
-    }
-}
-
-float DetourCrowdManager::GetAreaCost(unsigned filterID, unsigned areaID) const
-{
-    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;
-
-            // Draw CrowdAgent shape (from its radius & height)
-            CrowdAgent* crowdAgent = static_cast<CrowdAgent*>(ag->params.userData);
-            crowdAgent->DrawDebugGeometry(debug, depthTest);
-
-            // Draw move target if any
-            if (crowdAgent->GetTargetState() == CROWD_AGENT_TARGET_NONE)
-                continue;
-
-            Color color(0.6f, 0.2f, 0.2f, 1.0f);
-
-            // Draw 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);
-
-            // Draw target circle
-            debug->AddSphere(Sphere(pos2, 0.5f), color, depthTest);
-        }
-    }
-}
-
-void DetourCrowdManager::DrawDebugGeometry(bool depthTest)
-{
-    Scene* scene = GetScene();
-    if (scene)
-    {
-        DebugRenderer* debug = scene->GetComponent<DebugRenderer>();
-        if (debug)
-            DrawDebugGeometry(debug, depthTest);
-    }
-}
-
-bool DetourCrowdManager::CreateCrowd()
-{
-    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;
-    params.userData = agent;
-    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& 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;
-}
-
-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::OnSceneSet(Scene* scene)
-{
-    // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference
-    // to the scene's NavigationMesh
-    if (scene)
-    {
-        SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
-        NavigationMesh* mesh = GetScene()->GetComponent<NavigationMesh>();
-        if (!mesh)
-            mesh = GetScene()->GetComponent<DynamicNavigationMesh>();
-        if (mesh)
-        {
-            SubscribeToEvent(mesh, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
-            SetNavigationMesh(mesh);
-        }
-        else
-            LOGERROR("DetourCrowdManager requires an existing navigation mesh");
-    }
-    else
-    {
-        UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
-        UnsubscribeFromEvent(E_NAVIGATION_MESH_REBUILT);
-        navigationMesh_.Reset();
-    }
-
-}
-
-}

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

@@ -1,146 +0,0 @@
-//
-// 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 Atomic
-{
-
-class CrowdAgent;
-class NavigationMesh;
-
-enum NavigationQuality
-{
-    NAVIGATIONQUALITY_LOW = 0,
-    NAVIGATIONQUALITY_MEDIUM = 1,
-    NAVIGATIONQUALITY_HIGH = 2
-};
-
-enum NavigationPushiness
-{
-    PUSHINESS_LOW = 0,
-    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 ATOMIC_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);
-    /// Set the crowd move target. The move target is applied to all crowd agents within the id range, excluding crowd agent which does not have acceleration.
-    void SetCrowdTarget(const Vector3& position, int startId = 0, int endId = M_MAX_INT);
-    /// Reset the crowd move target to all crowd agents within the id range, excluding crowd agent which does not have acceleration.
-    void ResetCrowdTarget(int startId = 0, int endId = M_MAX_INT);
-    /// Set the crowd move velocity. The move velocity is applied to all crowd agents within the id range, excluding crowd agent which does not have acceleration.
-    void SetCrowdVelocity(const Vector3& velocity, int startId = 0, int endId = M_MAX_INT);
-
-    /// Get the Navigation mesh assigned to the crowd.
-    NavigationMesh* GetNavigationMesh() const { return navigationMesh_; }
-    /// 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);
-    /// Add debug geometry to the debug renderer.
-    void DrawDebugGeometry(bool depthTest);
-    /// Get the currently included agents.
-    const 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& targetRef);
-
-    /// Get the closest walkable position.
-    Vector3 GetClosestWalkablePosition(Vector3 pos) const;
-
-protected:
-    /// Update the crowd simulation.
-    void Update(float delta);
-    /// Handle scene being assigned.
-    virtual void OnSceneSet(Scene* scene);
-    /// Get the detour crowd agent.
-    const dtCrowdAgent* GetCrowdAgent(int agent);
-    /// Get the internal detour crowd component.
-    dtCrowd* GetCrowd() const { return crowd_; }
-
-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_;
-};
-
-}

+ 34 - 38
Source/Atomic/Navigation/DynamicNavigationMesh.cpp

@@ -22,35 +22,31 @@
 
 
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
-#include "../Navigation/DynamicNavigationMesh.h"
 
 
-#include "../Math/BoundingBox.h"
 #include "../Core/Context.h"
 #include "../Core/Context.h"
-#include "../Navigation/CrowdAgent.h"
+#include "../Core/Profiler.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../IO/MemoryBuffer.h"
 #include "../IO/MemoryBuffer.h"
+#include "../Navigation/CrowdAgent.h"
+#include "../Navigation/DynamicNavigationMesh.h"
 #include "../Navigation/NavArea.h"
 #include "../Navigation/NavArea.h"
 #include "../Navigation/NavBuildData.h"
 #include "../Navigation/NavBuildData.h"
 #include "../Navigation/NavigationEvents.h"
 #include "../Navigation/NavigationEvents.h"
-#include "../Scene/Node.h"
 #include "../Navigation/Obstacle.h"
 #include "../Navigation/Obstacle.h"
 #include "../Navigation/OffMeshConnection.h"
 #include "../Navigation/OffMeshConnection.h"
-#include "../Core/Profiler.h"
+#include "../Scene/Node.h"
 #include "../Scene/Scene.h"
 #include "../Scene/Scene.h"
 #include "../Scene/SceneEvents.h"
 #include "../Scene/SceneEvents.h"
 
 
 #include <LZ4/lz4.h>
 #include <LZ4/lz4.h>
-#include <cfloat>
 #include <Detour/include/DetourNavMesh.h>
 #include <Detour/include/DetourNavMesh.h>
 #include <Detour/include/DetourNavMeshBuilder.h>
 #include <Detour/include/DetourNavMeshBuilder.h>
-#include <Detour/include/DetourNavMeshQuery.h>
 #include <DetourTileCache/include/DetourTileCache.h>
 #include <DetourTileCache/include/DetourTileCache.h>
 #include <DetourTileCache/include/DetourTileCacheBuilder.h>
 #include <DetourTileCache/include/DetourTileCacheBuilder.h>
 #include <Recast/include/Recast.h>
 #include <Recast/include/Recast.h>
-#include <Recast/include/RecastAlloc.h>
 
 
-//DebugNew is deliberately not used because the macro 'free' conflicts DetourTileCache's LinearAllocator interface
+// DebugNew is deliberately not used because the macro 'free' conflicts with DetourTileCache's LinearAllocator interface
 //#include "../DebugNew.h"
 //#include "../DebugNew.h"
 
 
 #define TILECACHE_MAXLAYERS 128
 #define TILECACHE_MAXLAYERS 128
@@ -72,7 +68,7 @@ struct TileCompressor : public dtTileCacheCompressor
 {
 {
     virtual int maxCompressedSize(const int bufferSize)
     virtual int maxCompressedSize(const int bufferSize)
     {
     {
-        return (int)(bufferSize* 1.05f);
+        return (int)(bufferSize * 1.05f);
     }
     }
 
 
     virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
     virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
@@ -136,9 +132,9 @@ struct MeshProcess : public dtTileCacheMeshProcess
                     offMeshVertices_.Push(start);
                     offMeshVertices_.Push(start);
                     offMeshVertices_.Push(end);
                     offMeshVertices_.Push(end);
                     offMeshRadii_.Push(connection->GetRadius());
                     offMeshRadii_.Push(connection->GetRadius());
-                    offMeshFlags_.Push(connection->GetMask());
+                    offMeshFlags_.Push((unsigned short)connection->GetMask());
                     offMeshAreas_.Push((unsigned char)connection->GetAreaID());
                     offMeshAreas_.Push((unsigned char)connection->GetAreaID());
-                    offMeshDir_.Push(connection->IsBidirectional() ? DT_OFFMESH_CON_BIDIR : 0);
+                    offMeshDir_.Push((unsigned char)(connection->IsBidirectional() ? DT_OFFMESH_CON_BIDIR : 0));
                 }
                 }
             }
             }
             params->offMeshConCount = offMeshRadii_.Size();
             params->offMeshConCount = offMeshRadii_.Size();
@@ -169,7 +165,8 @@ struct LinearAllocator : public dtTileCacheAlloc
     int top;
     int top;
     int high;
     int high;
 
 
-    LinearAllocator(const int cap) : buffer(0), capacity(0), top(0), high(0)
+    LinearAllocator(const int cap) :
+        buffer(0), capacity(0), top(0), high(0)
     {
     {
         resize(cap);
         resize(cap);
     }
     }
@@ -281,7 +278,7 @@ bool DynamicNavigationMesh::Build()
         numTilesZ_ = (gridH + 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
         // 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 maxTiles = NextPowerOfTwo((unsigned)(numTilesX_ * numTilesZ_)) * TILECACHE_MAXLAYERS;
         unsigned tileBits = 0;
         unsigned tileBits = 0;
         unsigned temp = maxTiles;
         unsigned temp = maxTiles;
         while (temp > 1)
         while (temp > 1)
@@ -290,7 +287,7 @@ bool DynamicNavigationMesh::Build()
             ++tileBits;
             ++tileBits;
         }
         }
 
 
-        unsigned maxPolys = 1 << (22 - tileBits);
+        unsigned maxPolys = (unsigned)(1 << (22 - tileBits));
 
 
         dtNavMeshParams params;
         dtNavMeshParams params;
         rcVcopy(params.orig, &boundingBox_.min_.x_);
         rcVcopy(params.orig, &boundingBox_.min_.x_);
@@ -356,7 +353,7 @@ bool DynamicNavigationMesh::Build()
                 {
                 {
                     dtCompressedTileRef tileRef;
                     dtCompressedTileRef tileRef;
                     int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef);
                     int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef);
-                    if (dtStatusFailed(status))
+                    if (dtStatusFailed((dtStatus)status))
                     {
                     {
                         dtFree(tiles[i].data);
                         dtFree(tiles[i].data);
                         tiles[i].data = 0x0;
                         tiles[i].data = 0x0;
@@ -369,7 +366,7 @@ bool DynamicNavigationMesh::Build()
         }
         }
 
 
         // For a full build it's necessary to update the nav mesh
         // For a full build it's necessary to update the nav mesh
-        // not doing so will cause dependent components to crash, like DetourCrowdManager
+        // not doing so will cause dependent components to crash, like CrowdManager
         tileCache_->update(0, navMesh_);
         tileCache_->update(0, navMesh_);
 
 
         LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles");
         LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles");
@@ -446,7 +443,7 @@ bool DynamicNavigationMesh::Build(const BoundingBox& boundingBox)
             {
             {
                 dtCompressedTileRef tileRef;
                 dtCompressedTileRef tileRef;
                 int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef);
                 int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef);
-                if (dtStatusFailed(status))
+                if (dtStatusFailed((dtStatus)status))
                 {
                 {
                     dtFree(tiles[i].data);
                     dtFree(tiles[i].data);
                     tiles[i].data = 0x0;
                     tiles[i].data = 0x0;
@@ -492,12 +489,9 @@ void DynamicNavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTe
                     dtPoly* poly = tile->polys + i;
                     dtPoly* poly = tile->polys + i;
                     for (unsigned j = 0; j < poly->vertCount; ++j)
                     for (unsigned j = 0; j < poly->vertCount; ++j)
                     {
                     {
-                        debug->AddLine(
-                            worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[j] * 3]),
+                        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]),
                             worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[(j + 1) % poly->vertCount] * 3]),
-                            Color::YELLOW,
-                            depthTest
-                            );
+                            Color::YELLOW, depthTest);
                     }
                     }
                 }
                 }
             }
             }
@@ -611,7 +605,7 @@ void DynamicNavigationMesh::SetNavigationDataAttr(const PODVector<unsigned char>
         buffer.Read(&header, sizeof(dtTileCacheLayerHeader));
         buffer.Read(&header, sizeof(dtTileCacheLayerHeader));
         const int dataSize = buffer.ReadInt();
         const int dataSize = buffer.ReadInt();
         unsigned char* data = (unsigned char*)dtAlloc(dataSize, DT_ALLOC_PERM);
         unsigned char* data = (unsigned char*)dtAlloc(dataSize, DT_ALLOC_PERM);
-        buffer.Read(data, dataSize);
+        buffer.Read(data, (unsigned)dataSize);
 
 
         if (dtStatusFailed(tileCache_->addTile(data, dataSize, DT_TILE_FREE_DATA, 0)))
         if (dtStatusFailed(tileCache_->addTile(data, dataSize, DT_TILE_FREE_DATA, 0)))
         {
         {
@@ -659,7 +653,7 @@ PODVector<unsigned char> DynamicNavigationMesh::GetNavigationDataAttr() const
                     // The header conveniently has the majority of the information required
                     // The header conveniently has the majority of the information required
                     ret.Write(tile->header, sizeof(dtTileCacheLayerHeader));
                     ret.Write(tile->header, sizeof(dtTileCacheLayerHeader));
                     ret.WriteInt(tile->dataSize);
                     ret.WriteInt(tile->dataSize);
-                    ret.Write(tile->data, tile->dataSize);
+                    ret.Write(tile->data, (unsigned)tile->dataSize);
                 }
                 }
             }
             }
         }
         }
@@ -676,15 +670,13 @@ int DynamicNavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryLis
     float tileEdgeLength = (float)tileSize_ * cellSize_;
     float tileEdgeLength = (float)tileSize_ * cellSize_;
 
 
     BoundingBox tileBoundingBox(Vector3(
     BoundingBox tileBoundingBox(Vector3(
-        boundingBox_.min_.x_ + tileEdgeLength * (float)x,
-        boundingBox_.min_.y_,
-        boundingBox_.min_.z_ + tileEdgeLength * (float)z
-        ),
+            boundingBox_.min_.x_ + tileEdgeLength * (float)x,
+            boundingBox_.min_.y_,
+            boundingBox_.min_.z_ + tileEdgeLength * (float)z),
         Vector3(
         Vector3(
-        boundingBox_.min_.x_ + tileEdgeLength * (float)(x + 1),
-        boundingBox_.max_.y_,
-        boundingBox_.min_.z_ + tileEdgeLength * (float)(z + 1)
-        ));
+            boundingBox_.min_.x_ + tileEdgeLength * (float)(x + 1),
+            boundingBox_.max_.y_,
+            boundingBox_.min_.z_ + tileEdgeLength * (float)(z + 1)));
 
 
     DynamicNavBuildData build(allocator_);
     DynamicNavBuildData build(allocator_);
 
 
@@ -768,7 +760,8 @@ int DynamicNavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryLis
 
 
     // area volumes
     // area volumes
     for (unsigned i = 0; i < build.navAreas_.Size(); ++i)
     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_);
+        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 (this->partitionType_ == NAVMESH_PARTITION_WATERSHED)
     {
     {
@@ -800,7 +793,8 @@ int DynamicNavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryLis
         return 0;
         return 0;
     }
     }
 
 
-    if (!rcBuildHeightfieldLayers(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.walkableHeight, *build.heightFieldLayers_))
+    if (!rcBuildHeightfieldLayers(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.walkableHeight,
+        *build.heightFieldLayers_))
     {
     {
         LOGERROR("Could not build height field layers");
         LOGERROR("Could not build height field layers");
         return 0;
         return 0;
@@ -830,7 +824,9 @@ int DynamicNavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryLis
         header.hmin = (unsigned short)layer->hmin;
         header.hmin = (unsigned short)layer->hmin;
         header.hmax = (unsigned short)layer->hmax;
         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)))
+        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");
             LOGERROR("Failed to build tile cache layers");
             return 0;
             return 0;
@@ -901,7 +897,7 @@ void DynamicNavigationMesh::AddObstacle(Obstacle* obstacle, bool silent)
         rcVcopy(pos, &obsPos.x_);
         rcVcopy(pos, &obsPos.x_);
         dtObstacleRef refHolder;
         dtObstacleRef refHolder;
 
 
-        // Because dtTileCache doesn't process obstacle requests while updating tiles 
+        // Because dtTileCache doesn't process obstacle requests while updating tiles
         // it's necessary update until sufficient request space is available
         // it's necessary update until sufficient request space is available
         while (tileCache_->isObstacleQueueFull())
         while (tileCache_->isObstacleQueueFull())
             tileCache_->update(1, navMesh_);
             tileCache_->update(1, navMesh_);
@@ -941,7 +937,7 @@ void DynamicNavigationMesh::RemoveObstacle(Obstacle* obstacle, bool silent)
 {
 {
     if (tileCache_ && obstacle->obstacleId_ > 0)
     if (tileCache_ && obstacle->obstacleId_ > 0)
     {
     {
-        // Because dtTileCache doesn't process obstacle requests while updating tiles 
+        // Because dtTileCache doesn't process obstacle requests while updating tiles
         // it's necessary update until sufficient request space is available
         // it's necessary update until sufficient request space is available
         while (tileCache_->isObstacleQueueFull())
         while (tileCache_->isObstacleQueueFull())
             tileCache_->update(1, navMesh_);
             tileCache_->update(1, navMesh_);

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

@@ -41,6 +41,7 @@ class Obstacle;
 class ATOMIC_API DynamicNavigationMesh : public NavigationMesh
 class ATOMIC_API DynamicNavigationMesh : public NavigationMesh
 {
 {
     OBJECT(DynamicNavigationMesh)
     OBJECT(DynamicNavigationMesh)
+
     friend class Obstacle;
     friend class Obstacle;
     friend struct MeshProcess;
     friend struct MeshProcess;
 
 
@@ -69,11 +70,13 @@ public:
 
 
     /// Set the maximum number of obstacles allowed.
     /// Set the maximum number of obstacles allowed.
     void SetMaxObstacles(unsigned maxObstacles) { maxObstacles_ = maxObstacles; }
     void SetMaxObstacles(unsigned maxObstacles) { maxObstacles_ = maxObstacles; }
+
     /// Return the maximum number of obstacles allowed.
     /// Return the maximum number of obstacles allowed.
     unsigned GetMaxObstacles() const { return maxObstacles_; }
     unsigned GetMaxObstacles() const { return maxObstacles_; }
 
 
     /// Draw debug geometry for Obstacles.
     /// Draw debug geometry for Obstacles.
     void SetDrawObstacles(bool enable) { drawObstacles_ = enable; }
     void SetDrawObstacles(bool enable) { drawObstacles_ = enable; }
+
     /// Return whether to draw Obstacles.
     /// Return whether to draw Obstacles.
     bool GetDrawObstacles() const { return drawObstacles_; }
     bool GetDrawObstacles() const { return drawObstacles_; }
 
 

+ 43 - 44
Source/Atomic/Navigation/NavArea.cpp

@@ -22,67 +22,66 @@
 
 
 #include "../Precompiled.h"
 #include "../Precompiled.h"
 
 
-#include "../Scene/Component.h"
 #include "../Core/Context.h"
 #include "../Core/Context.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../Graphics/DebugRenderer.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 #include "../Navigation/NavArea.h"
 #include "../Navigation/NavArea.h"
 #include "../Scene/Node.h"
 #include "../Scene/Node.h"
-#include "../Container/Str.h"
 
 
 namespace Atomic
 namespace Atomic
 {
 {
-    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;
+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_AREA_ID = 0;
 
 
-    NavArea::NavArea(Context* context) :
-        Component(context),
-        areaID_(DEFAULT_AREA_ID),
-        boundingBox_(DEFAULT_BOUNDING_BOX_MIN, DEFAULT_BOUNDING_BOX_MAX)
-    {
-    }
+extern const char* NAVIGATION_CATEGORY;
 
 
-    NavArea::~NavArea()
-    {
-    }
-    
-    void NavArea::RegisterObject(Context* context)
-    {
-        context->RegisterFactory<NavArea>(NAVIGATION_CATEGORY);
+NavArea::NavArea(Context* context) :
+    Component(context),
+    areaID_(DEFAULT_AREA_ID),
+    boundingBox_(DEFAULT_BOUNDING_BOX_MIN, DEFAULT_BOUNDING_BOX_MAX)
+{
+}
 
 
-        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);
-    }
+NavArea::~NavArea()
+{
+}
 
 
-    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();
-    }
+void NavArea::RegisterObject(Context* context)
+{
+    context->RegisterFactory<NavArea>(NAVIGATION_CATEGORY);
 
 
-    BoundingBox NavArea::GetWorldBoundingBox() const
+    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;
         Matrix3x4 mat;
         mat.SetTranslation(node_->GetWorldPosition());
         mat.SetTranslation(node_->GetWorldPosition());
-        return boundingBox_.Transformed(mat);
+        debug->AddBoundingBox(boundingBox_, mat, Color::GREEN, depthTest);
     }
     }
+}
 
 
-    void NavArea::DrawDebugGeometry(DebugRenderer* debug, bool depthTest) 
-    {
-        if (debug && IsEnabledEffective())
-        {
-            Matrix3x4 mat;
-            mat.SetTranslation(node_->GetWorldPosition());
-            debug->AddBoundingBox(boundingBox_, mat, Color::GREEN, depthTest);
-        }
-    }
 }
 }

+ 33 - 31
Source/Atomic/Navigation/NavArea.h

@@ -22,45 +22,47 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "../Scene/Component.h"
 #include "../Math/BoundingBox.h"
 #include "../Math/BoundingBox.h"
+#include "../Scene/Component.h"
 
 
 namespace Atomic
 namespace Atomic
 {
 {
-    class ATOMIC_API NavArea : public Component
-    {
-        OBJECT(NavArea);
 
 
-    public:
-        /// Construct.
-        NavArea(Context*);
-        /// Destruct.
-        virtual ~NavArea();
-        /// Register object factory and attributes.
-        static void RegisterObject(Context*);
+class ATOMIC_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);
 
 
-        /// Render debug geometry for the bounds.
-        virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+    /// Get the bounding box of this navigation area, in local space.
+    BoundingBox GetBoundingBox() const { return boundingBox_; }
 
 
-        /// Get the area id for this volume.
-        unsigned GetAreaID() const { return (unsigned)areaID_; }
-        /// Set the area id for this volume.
-        void SetAreaID(unsigned newID);
+    /// Set the bounding box of this area, in local space.
+    void SetBoundingBox(const BoundingBox& bnds) { boundingBox_ = bnds; }
 
 
-        /// 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;
 
 
-        /// Get the bounds of this navigation area in world space.
-        BoundingBox GetWorldBoundingBox() const;
+private:
+    /// Bounds of area to mark.
+    BoundingBox boundingBox_;
+    /// Area id to assign to the marked area.
+    unsigned char areaID_;
+};
 
 
-    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_;
-    };
 }
 }

Some files were not shown because too many files changed in this diff