浏览代码

Non-Graphics updates

Josh Engebretson 10 年之前
父节点
当前提交
0fdeea3ccf

+ 0 - 191
Source/Atomic/Container/HashTable.h

@@ -1,191 +0,0 @@
-//
-// Copyright (c) 2008-2014 the Urho3D project.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#pragma once
-
-#include "../Container/Allocator.h"
-#include "../Container/Vector.h"
-
-namespace Atomic
-{
-
-/// Hash table with fixed bucket count. Does not support iteration. Should only be used when performance is critical, as HashMap is much more user-friendly.
-template <class T, unsigned U> class HashTable
-{
-public:
-    /// Hash table node.
-    struct Node
-    {
-        /// Construct.
-        Node(unsigned hash, const T& value, Node* next) :
-            hash_(hash),
-            value_(value),
-            next_(next)
-        {
-        }
-        
-        /// Hash value.
-        unsigned hash_;
-        /// Node value.
-        T value_;
-        /// Next node in bucket.
-        Node* next_;
-    };
-    
-    /// Construct empty.
-    HashTable() :
-        allocator_(0)
-    {
-        for (unsigned i = 0; i < U; ++i)
-            ptrs_[i] = 0;
-    }
-    
-    /// Destruct.
-    ~HashTable()
-    {
-        Clear();
-        AllocatorUninitialize(allocator_);
-    }
-    
-    /// Insert by hash value. If value with same hash already exists, it is replaced.
-    void Insert(unsigned hash, const T& value)
-    {
-        unsigned bucket = hash & (U - 1);
-        
-        if (!allocator_)
-            allocator_ = AllocatorInitialize(sizeof(Node));
-        
-        Node* ptr = ptrs_[bucket];
-        while (ptr)
-        {
-            if (ptr->hash_ == hash)
-            {
-                ptr->value_ = value;
-                return;
-            }
-            ptr = ptr->next_;
-        }
-        
-        Node* newNode = static_cast<Node*>(AllocatorReserve(allocator_));
-        // Insert at the top of the bucket, connect to the previous top node if exists
-        new(newNode) Node(hash, value, ptrs_[bucket]);
-        ptrs_[bucket] = newNode;
-    }
-    
-    /// Remove by hash value. Return true if was found and removed.
-    bool Erase(unsigned hash)
-    {
-        unsigned bucket = hash & (U - 1);
-        
-        Node* ptr = ptrs_[bucket];
-        while (ptr)
-        {
-            if (ptr->hash_ == hash)
-            {
-                (ptr)->~Node();
-                AllocatorFree(allocator_, ptr);
-                return true;
-            }
-            else
-                ptr = ptr->next_;
-        }
-        
-        return false;
-    }
-    
-    /// Remove all values.
-    void Clear()
-    {
-        for (unsigned i = 0; i < U; ++i)
-        {
-            Node* ptr = ptrs_[i];
-            while (ptr)
-            {
-                Node* next = ptr->next_;
-                (ptr)->~Node();
-                AllocatorFree(allocator_, ptr);
-                ptr = next;
-            }
-        }
-    }
-    
-    /// Find by hash value. Return pointer if was found or null if not found.
-    T* Find(unsigned hash) const
-    {
-        unsigned bucket = hash & (U - 1);
-        
-        Node* ptr = ptrs_[bucket];
-        while (ptr)
-        {
-            if (ptr->hash_ == hash)
-                return &ptr->value_;
-            else
-                ptr = ptr->next_;
-        }
-        
-        return 0;
-    }
-    
-    /// Return all the keys.
-    PODVector<unsigned> Keys() const
-    {
-        PODVector<unsigned> ret;
-
-        for (unsigned i = 0; i < U; ++i)
-        {
-            Node* ptr = ptrs_[i];
-            while (ptr)
-            {
-                ret.Push(ptr->hash_);
-                ptr = ptr->next_;
-            }
-        }
-
-        return ret;
-    }
-
-    /// Return pointers to all values.
-    PODVector<T*> Values() const
-    {
-        PODVector<T*> ret;
-        
-        for (unsigned i = 0; i < U; ++i)
-        {
-            Node* ptr = ptrs_[i];
-            while (ptr)
-            {
-                ret.Push(&ptr->value_);
-                ptr = ptr->next_;
-            }
-        }
-        
-        return ret;
-    }
-    
-private:
-    /// Allocator.
-    AllocatorBlock* allocator_;
-    /// Bucket pointers, fixed size.
-    Node* ptrs_[U];
-};
-
-}

+ 21 - 1
Source/Atomic/Container/Str.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -183,6 +183,16 @@ String& String::operator += (short rhs)
     return *this += String(rhs);
 }
 
+String& String::operator += (long rhs)
+{
+    return *this += String(rhs);
+}
+
+String& String::operator += (long long rhs)
+{
+    return *this += String(rhs);
+}
+
 String& String::operator += (unsigned rhs)
 {
     return *this += String(rhs);
@@ -193,6 +203,16 @@ String& String::operator += (unsigned short rhs)
     return *this += String(rhs);
 }
 
+String& String::operator += (unsigned long rhs)
+{
+    return *this += String(rhs);
+}
+
+String& String::operator += (unsigned long long rhs)
+{
+    return *this += String(rhs);
+}
+
 String& String::operator += (float rhs)
 {
     return *this += String(rhs);

+ 10 - 2
Source/Atomic/Container/Str.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -206,10 +206,18 @@ public:
     String& operator += (int rhs);
     /// Add-assign a short integer.
     String& operator += (short rhs);
+    /// Add-assign a long integer.
+    String& operator += (long rhs);
+    /// Add-assign a long long integer.
+    String& operator += (long long rhs);
     /// Add-assign an unsigned integer.
     String& operator += (unsigned rhs);
     /// Add-assign a short unsigned integer.
     String& operator += (unsigned short rhs);
+    /// Add-assign a long unsigned integer.
+    String& operator += (unsigned long rhs);
+    /// Add-assign a long long unsigned integer.
+    String& operator += (unsigned long long rhs);
     /// Add-assign a float.
     String& operator += (float rhs);
     /// Add-assign a bool.
@@ -439,7 +447,7 @@ public:
         const char* ptr = str;
         while (*ptr)
             ++ptr;
-        return ptr - str;
+        return (unsigned)(ptr - str);
         #endif
     }
     

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

@@ -176,7 +176,7 @@ public:
     }
     
     /// Create an object of the specific type.
-    virtual SharedPtr<Object>(CreateObject()) { return SharedPtr<Object>(new T(context_)); }
+    virtual SharedPtr<Object> CreateObject() { return SharedPtr<Object>(new T(context_)); }
 };
 
 /// Internal helper class for invoking event handler functions.

+ 14 - 11
Source/Atomic/Core/ProcessUtils.cpp

@@ -160,18 +160,21 @@ void OpenConsoleWindow()
 
 void PrintUnicode(const String& str, bool error)
 {
-#if !defined(ANDROID) && !defined(IOS)
-#ifdef WIN32
-    HANDLE stream = GetStdHandle(error ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
-    if (stream == INVALID_HANDLE_VALUE)
-        return;
-    WString strW(str);
-    DWORD charsWritten;
-    WriteConsoleW(stream, strW.CString(), strW.Length(), &charsWritten, 0);
-    
-    if (IsDebuggerPresent())
+    #if !defined(ANDROID) && !defined(IOS)
+    #ifdef WIN32
+    // If the output stream has been redirected, use fprintf instead of WriteConsoleW,
+    // though it means that proper Unicode output will not work
+    FILE* out = error ? stderr : stdout;
+    if (!_isatty(_fileno(out)))
+        fprintf(out, "%s", str.CString());
+    else
     {
-        OutputDebugString(str.CString());
+        HANDLE stream = GetStdHandle(error ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
+        if (stream == INVALID_HANDLE_VALUE)
+            return;
+        WString strW(str);
+        DWORD charsWritten;
+        WriteConsoleW(stream, strW.CString(), strW.Length(), &charsWritten, 0);
     }
     #else
     fprintf(error ? stderr : stdout, "%s", str.CString());

+ 77 - 77
Source/Atomic/Core/StringUtils.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -34,50 +34,50 @@ unsigned CountElements(const char* buffer, char separator)
 {
     if (!buffer)
         return 0;
-    
+
     const char* endPos = buffer + String::CStringLength(buffer);
     const char* pos = buffer;
     unsigned ret = 0;
-    
+
     while (pos < endPos)
     {
         if (*pos != separator)
             break;
         ++pos;
     }
-    
+
     while (pos < endPos)
     {
         const char* start = pos;
-        
+
         while (start < endPos)
         {
             if (*start == separator)
                 break;
-            
+
             ++start;
         }
-        
+
         if (start == endPos)
         {
             ++ret;
             break;
         }
-        
+
         const char* end = start;
-        
+
         while (end < endPos)
         {
             if (*end != separator)
                 break;
-            
+
             ++end;
         }
-        
+
         ++ret;
         pos = end;
     }
-    
+
     return ret;
 }
 
@@ -89,7 +89,7 @@ bool ToBool(const String& source)
 bool ToBool(const char* source)
 {
     unsigned length = String::CStringLength(source);
-    
+
     for (unsigned i = 0; i < length; ++i)
     {
         char c = tolower(source[i]);
@@ -98,7 +98,7 @@ bool ToBool(const char* source)
         else if (c != ' ' && c != '\t')
             break;
     }
-    
+
     return false;
 }
 
@@ -111,7 +111,7 @@ int ToInt(const char* source)
 {
     if (!source)
         return 0;
-    
+
     // Explicitly ask for base 10 to prevent source starts with '0' or '0x' from being converted to base 8 or base 16, respectively
     return strtol(source, 0, 10);
 }
@@ -125,7 +125,7 @@ unsigned ToUInt(const char* source)
 {
     if (!source)
         return 0;
-    
+
     return strtoul(source, 0, 10);
 }
 
@@ -138,7 +138,7 @@ float ToFloat(const char* source)
 {
     if (!source)
         return 0;
-    
+
     return (float)strtod(source, 0);
 }
 
@@ -150,18 +150,18 @@ Color ToColor(const String& source)
 Color ToColor(const char* source)
 {
     Color ret;
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 3)
         return ret;
-    
+
     char* ptr = (char*)source;
     ret.r_ = (float)strtod(ptr, &ptr);
     ret.g_ = (float)strtod(ptr, &ptr);
     ret.b_ = (float)strtod(ptr, &ptr);
     if (elements > 3)
         ret.a_ = (float)strtod(ptr, &ptr);
-    
+
     return ret;
 }
 
@@ -173,17 +173,17 @@ IntRect ToIntRect(const String& source)
 IntRect ToIntRect(const char* source)
 {
     IntRect ret(IntRect::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 4)
         return ret;
-    
+
     char* ptr = (char*)source;
-    ret.left_ = strtol(ptr, &ptr, 10);
-    ret.top_ = strtol(ptr, &ptr, 10);
-    ret.right_ = strtol(ptr, &ptr, 10);
-    ret.bottom_ = strtol(ptr, &ptr, 10);
-    
+    ret.left_ = (int)strtol(ptr, &ptr, 10);
+    ret.top_ = (int)strtol(ptr, &ptr, 10);
+    ret.right_ = (int)strtol(ptr, &ptr, 10);
+    ret.bottom_ = (int)strtol(ptr, &ptr, 10);
+
     return ret;
 }
 
@@ -195,15 +195,15 @@ IntVector2 ToIntVector2(const String& source)
 IntVector2 ToIntVector2(const char* source)
 {
     IntVector2 ret(IntVector2::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 2)
         return ret;
-    
+
     char* ptr = (char*)source;
-    ret.x_ = strtol(ptr, &ptr, 10);
-    ret.y_ = strtol(ptr, &ptr, 10);
-    
+    ret.x_ = (int)strtol(ptr, &ptr, 10);
+    ret.y_ = (int)strtol(ptr, &ptr, 10);
+
     return ret;
 }
 
@@ -215,17 +215,17 @@ Rect ToRect(const String& source)
 Rect ToRect(const char* source)
 {
     Rect ret(Rect::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 4)
         return ret;
-    
+
     char* ptr = (char*)source;
     ret.min_.x_ = (float)strtod(ptr, &ptr);
     ret.min_.y_ = (float)strtod(ptr, &ptr);
     ret.max_.x_ = (float)strtod(ptr, &ptr);
     ret.max_.y_ = (float)strtod(ptr, &ptr);
-    
+
     return ret;
 }
 
@@ -238,7 +238,7 @@ Quaternion ToQuaternion(const char* source)
 {
     unsigned elements = CountElements(source, ' ');
     char* ptr = (char*)source;
-    
+
     if (elements < 3)
         return Quaternion::IDENTITY;
     else if (elements < 4)
@@ -248,7 +248,7 @@ Quaternion ToQuaternion(const char* source)
         x = (float)strtod(ptr, &ptr);
         y = (float)strtod(ptr, &ptr);
         z = (float)strtod(ptr, &ptr);
-        
+
         return Quaternion(x, y, z);
     }
     else
@@ -259,7 +259,7 @@ Quaternion ToQuaternion(const char* source)
         ret.x_ = (float)strtod(ptr, &ptr);
         ret.y_ = (float)strtod(ptr, &ptr);
         ret.z_ = (float)strtod(ptr, &ptr);
-        
+
         return ret;
     }
 }
@@ -272,15 +272,15 @@ Vector2 ToVector2(const String& source)
 Vector2 ToVector2(const char* source)
 {
     Vector2 ret(Vector2::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 2)
         return ret;
-    
+
     char* ptr = (char*)source;
     ret.x_ = (float)strtod(ptr, &ptr);
     ret.y_ = (float)strtod(ptr, &ptr);
-    
+
     return ret;
 }
 
@@ -292,16 +292,16 @@ Vector3 ToVector3(const String& source)
 Vector3 ToVector3(const char* source)
 {
     Vector3 ret(Vector3::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 3)
         return ret;
-    
+
     char* ptr = (char*)source;
     ret.x_ = (float)strtod(ptr, &ptr);
     ret.y_ = (float)strtod(ptr, &ptr);
     ret.z_ = (float)strtod(ptr, &ptr);
-    
+
     return ret;
 }
 
@@ -313,20 +313,20 @@ Vector4 ToVector4(const String& source, bool allowMissingCoords)
 Vector4 ToVector4(const char* source, bool allowMissingCoords)
 {
     Vector4 ret(Vector4::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     char* ptr = (char*)source;
-    
+
     if (!allowMissingCoords)
     {
         if (elements < 4)
             return ret;
-        
+
         ret.x_ = (float)strtod(ptr, &ptr);
         ret.y_ = (float)strtod(ptr, &ptr);
         ret.z_ = (float)strtod(ptr, &ptr);
         ret.w_ = (float)strtod(ptr, &ptr);
-        
+
         return ret;
     }
     else
@@ -339,7 +339,7 @@ Vector4 ToVector4(const char* source, bool allowMissingCoords)
             ret.z_ = (float)strtod(ptr, &ptr);
         if (elements > 3)
             ret.w_ = (float)strtod(ptr, &ptr);
-        
+
         return ret;
     }
 }
@@ -353,25 +353,25 @@ Variant ToVectorVariant(const char* source)
 {
     Variant ret;
     unsigned elements = CountElements(source, ' ');
-    
+
     switch (elements)
     {
     case 1:
         ret.FromString(VAR_FLOAT, source);
         break;
-        
+
     case 2:
         ret.FromString(VAR_VECTOR2, source);
         break;
-        
+
     case 3:
         ret.FromString(VAR_VECTOR3, source);
         break;
-        
+
     case 4:
         ret.FromString(VAR_VECTOR4, source);
         break;
-        
+
     case 9:
         ret.FromString(VAR_MATRIX3, source);
         break;
@@ -384,7 +384,7 @@ Variant ToVectorVariant(const char* source)
         ret.FromString(VAR_MATRIX4, source);
         break;
     }
-    
+
     return ret;
 }
 
@@ -396,11 +396,11 @@ Matrix3 ToMatrix3(const String& source)
 Matrix3 ToMatrix3(const char* source)
 {
     Matrix3 ret(Matrix3::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 9)
         return ret;
-    
+
     char* ptr = (char*)source;
     ret.m00_ = (float)strtod(ptr, &ptr);
     ret.m01_ = (float)strtod(ptr, &ptr);
@@ -411,7 +411,7 @@ Matrix3 ToMatrix3(const char* source)
     ret.m20_ = (float)strtod(ptr, &ptr);
     ret.m21_ = (float)strtod(ptr, &ptr);
     ret.m22_ = (float)strtod(ptr, &ptr);
-    
+
     return ret;
 }
 
@@ -423,11 +423,11 @@ Matrix3x4 ToMatrix3x4(const String& source)
 Matrix3x4 ToMatrix3x4(const char* source)
 {
     Matrix3x4 ret(Matrix3x4::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 12)
         return ret;
-    
+
     char* ptr = (char*)source;
     ret.m00_ = (float)strtod(ptr, &ptr);
     ret.m01_ = (float)strtod(ptr, &ptr);
@@ -441,7 +441,7 @@ Matrix3x4 ToMatrix3x4(const char* source)
     ret.m21_ = (float)strtod(ptr, &ptr);
     ret.m22_ = (float)strtod(ptr, &ptr);
     ret.m23_ = (float)strtod(ptr, &ptr);
-    
+
     return ret;
 }
 
@@ -453,11 +453,11 @@ Matrix4 ToMatrix4(const String& source)
 Matrix4 ToMatrix4(const char* source)
 {
     Matrix4 ret(Matrix4::ZERO);
-    
+
     unsigned elements = CountElements(source, ' ');
     if (elements < 16)
         return ret;
-    
+
     char* ptr = (char*)source;
     ret.m00_ = (float)strtod(ptr, &ptr);
     ret.m01_ = (float)strtod(ptr, &ptr);
@@ -475,7 +475,7 @@ Matrix4 ToMatrix4(const char* source)
     ret.m31_ = (float)strtod(ptr, &ptr);
     ret.m32_ = (float)strtod(ptr, &ptr);
     ret.m33_ = (float)strtod(ptr, &ptr);
-    
+
     return ret;
 }
 
@@ -501,7 +501,7 @@ void BufferToString(String& dest, const void* data, unsigned size)
         // Room for separator
         if (i)
             ++length;
-        
+
         // Room for the value
         if (bytes[i] < 10)
             ++length;
@@ -510,16 +510,16 @@ void BufferToString(String& dest, const void* data, unsigned size)
         else
             length += 3;
     }
-    
+
     dest.Resize(length);
     unsigned index = 0;
-    
+
     // Convert values
     for (unsigned i = 0; i < size; ++i)
     {
         if (i)
             dest[index++] = ' ';
-        
+
         if (bytes[i] < 10)
         {
             dest[index++] = '0' + bytes[i];
@@ -550,14 +550,14 @@ void StringToBuffer(PODVector<unsigned char>& dest, const char* source)
         dest.Clear();
         return;
     }
-    
+
     unsigned size = CountElements(source, ' ');
     dest.Resize(size);
-    
+
     bool inSpace = true;
     unsigned index = 0;
     unsigned value = 0;
-    
+
     // Parse values
     const char* ptr = source;
     while (*ptr)
@@ -577,10 +577,10 @@ void StringToBuffer(PODVector<unsigned char>& dest, const char* source)
             dest[index++] = value;
             inSpace = true;
         }
-        
+
         ++ptr;
     }
-    
+
     // Write the final value
     if (!inSpace && index < size)
         dest[index] = value;
@@ -594,28 +594,28 @@ unsigned GetStringListIndex(const String& value, const String* strings, unsigned
 unsigned GetStringListIndex(const char* value, const String* strings, unsigned defaultIndex, bool caseSensitive)
 {
     unsigned i = 0;
-    
+
     while (!strings[i].Empty())
     {
         if (!strings[i].Compare(value, caseSensitive))
             return i;
         ++i;
     }
-    
+
     return defaultIndex;
 }
 
 unsigned GetStringListIndex(const char* value, const char** strings, unsigned defaultIndex, bool caseSensitive)
 {
     unsigned i = 0;
-    
+
     while (strings[i])
     {
         if (!String::Compare(value, strings[i], caseSensitive))
             return i;
         ++i;
     }
-    
+
     return defaultIndex;
 }
 

+ 24 - 24
Source/Atomic/Core/Timer.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -72,19 +72,19 @@ void Time::BeginFrame(float timeStep)
     ++frameNumber_;
     if (!frameNumber_)
         ++frameNumber_;
-    
+
     timeStep_ = timeStep;
-    
+
     Profiler* profiler = GetSubsystem<Profiler>();
     if (profiler)
         profiler->BeginFrame();
-    
+
     {
         PROFILE(BeginFrame);
-        
+
         // Frame begin event
         using namespace BeginFrame;
-        
+
         VariantMap& eventData = GetEventDataMap();
         eventData[P_FRAMENUMBER] = frameNumber_;
         eventData[P_TIMESTEP] = timeStep_;
@@ -96,11 +96,11 @@ void Time::EndFrame()
 {
     {
         PROFILE(EndFrame);
-        
+
         // Frame end event
         SendEvent(E_ENDFRAME);
     }
-    
+
     Profiler* profiler = GetSubsystem<Profiler>();
     if (profiler)
         profiler->EndFrame();
@@ -111,9 +111,9 @@ void Time::SetTimerPeriod(unsigned mSec)
     #ifdef WIN32
     if (timerPeriod_ > 0)
         timeEndPeriod(timerPeriod_);
-    
+
     timerPeriod_ = mSec;
-    
+
     if (timerPeriod_ > 0)
         timeBeginPeriod(timerPeriod_);
     #endif
@@ -127,13 +127,13 @@ float Time::GetElapsedTime()
 unsigned Time::GetSystemTime()
 {
     #ifdef WIN32
-    unsigned currentTime = timeGetTime();
+    unsigned currentTime = (unsigned)timeGetTime();
     #else
     struct timeval time;
     gettimeofday(&time, NULL);
-    unsigned currentTime = time.tv_sec * 1000 + time.tv_usec / 1000;
+    unsigned currentTime = (unsigned)(time.tv_sec * 1000 + time.tv_usec / 1000);
     #endif
-    
+
     return currentTime;
 }
 
@@ -167,28 +167,28 @@ Timer::Timer()
 unsigned Timer::GetMSec(bool reset)
 {
     #ifdef WIN32
-    unsigned currentTime = timeGetTime();
+    unsigned currentTime = (unsigned)timeGetTime();
     #else
     struct timeval time;
     gettimeofday(&time, NULL);
-    unsigned currentTime = time.tv_sec * 1000 + time.tv_usec / 1000;
+    unsigned currentTime = (unsigned)(time.tv_sec * 1000 + time.tv_usec / 1000);
     #endif
-    
+
     unsigned elapsedTime = currentTime - startTime_;
     if (reset)
         startTime_ = currentTime;
-    
+
     return elapsedTime;
 }
 
 void Timer::Reset()
 {
     #ifdef WIN32
-    startTime_ = timeGetTime();
+    startTime_ = (unsigned)timeGetTime();
     #else
     struct timeval time;
     gettimeofday(&time, NULL);
-    startTime_ = time.tv_sec * 1000 + time.tv_usec / 1000;
+    startTime_ = (unsigned)(time.tv_sec * 1000 + time.tv_usec / 1000);
     #endif
 }
 
@@ -200,7 +200,7 @@ HiresTimer::HiresTimer()
 long long HiresTimer::GetUSec(bool reset)
 {
     long long currentTime;
-    
+
     #ifdef WIN32
     if (supported)
     {
@@ -215,16 +215,16 @@ long long HiresTimer::GetUSec(bool reset)
     gettimeofday(&time, NULL);
     currentTime = time.tv_sec * 1000000LL + time.tv_usec;
     #endif
-    
+
     long long elapsedTime = currentTime - startTime_;
-    
+
     // Correct for possible weirdness with changing internal frequency
     if (elapsedTime < 0)
         elapsedTime = 0;
-    
+
     if (reset)
         startTime_ = currentTime;
-    
+
     return (elapsedTime * 1000000LL) / frequency;
 }
 

+ 2 - 2
Source/Atomic/Core/Variant.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -722,7 +722,7 @@ public:
     /// Return int or zero on type mismatch.
     int GetInt() const { return type_ == VAR_INT ? value_.int_ : 0; }
     /// Return unsigned int or zero on type mismatch.
-    int GetUInt() const { return type_ == VAR_INT ? (unsigned)value_.int_ : 0; }
+    unsigned GetUInt() const { return type_ == VAR_INT ? (unsigned)value_.int_ : 0; }
     /// Return StringHash or zero on type mismatch.
     StringHash GetStringHash() const { return StringHash(GetUInt()); }
     /// Return bool or false on type mismatch.

+ 71 - 19
Source/Atomic/Core/WorkQueue.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -160,6 +160,54 @@ void WorkQueue::AddWorkItem(SharedPtr<WorkItem> item)
     }
 }
 
+bool WorkQueue::RemoveWorkItem(SharedPtr<WorkItem> item)
+{
+    if (!item)
+        return false;
+
+    MutexLock lock(queueMutex_);
+    
+    // Can only remove successfully if the item was not yet taken by threads for execution
+    List<WorkItem*>::Iterator i = queue_.Find(item.Get());
+    if (i != queue_.End())
+    {
+        List<SharedPtr<WorkItem> >::Iterator j = workItems_.Find(item);
+        if (j != workItems_.End())
+        {
+            queue_.Erase(i);
+            ReturnToPool(item);
+            workItems_.Erase(j);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+unsigned WorkQueue::RemoveWorkItems(const Vector<SharedPtr<WorkItem> >& items)
+{
+    MutexLock lock(queueMutex_);
+    unsigned removed = 0;
+
+    for (Vector<SharedPtr<WorkItem> >::ConstIterator i = items.Begin(); i != items.End(); ++i)
+    {
+        List<WorkItem*>::Iterator j = queue_.Find(i->Get());
+        if (j != queue_.End())
+        {
+            List<SharedPtr<WorkItem> >::Iterator k = workItems_.Find(*i);
+            if (k != workItems_.End())
+            {
+                queue_.Erase(j);
+                ReturnToPool(*k);
+                workItems_.Erase(k);
+                ++removed;
+            }
+        }
+    }
+
+    return removed;
+}
+
 void WorkQueue::Pause()
 {
     if (!paused_)
@@ -296,24 +344,7 @@ void WorkQueue::PurgeCompleted(unsigned priority)
                 SendEvent(E_WORKITEMCOMPLETED, eventData);
             }
 
-            // Check if this was a pooled item and set it to usable
-            if ((*i)->pooled_)
-            {
-                // Reset the values to their defaults. This should 
-                // be safe to do here as the completed event has 
-                // already been handled and this is part of the 
-                // internal pool.
-                (*i)->start_ = NULL;
-                (*i)->end_ = NULL;
-                (*i)->aux_ = NULL;
-                (*i)->workFunction_ = NULL;
-                (*i)->priority_ = M_MAX_UNSIGNED;
-                (*i)->sendEvent_ = false;
-                (*i)->completed_ = false;
-
-                poolItems_.Push(*i);
-            }
-
+            ReturnToPool(*i);
             i = workItems_.Erase(i);
         }
         else
@@ -333,6 +364,27 @@ void WorkQueue::PurgePool()
     lastSize_ = currentSize;
 }
 
+void WorkQueue::ReturnToPool(SharedPtr<WorkItem>& item)
+{
+    // Check if this was a pooled item and set it to usable
+    if (item->pooled_)
+    {
+        // Reset the values to their defaults. This should 
+        // be safe to do here as the completed event has
+        // already been handled and this is part of the
+        // internal pool.
+        item->start_ = 0;
+        item->end_ = 0;
+        item->aux_ = 0;
+        item->workFunction_ = 0;
+        item->priority_ = M_MAX_UNSIGNED;
+        item->sendEvent_ = false;
+        item->completed_ = false;
+
+        poolItems_.Push(item);
+    }
+}
+
 void WorkQueue::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
 {
     // If no worker threads, complete low-priority work here

+ 7 - 1
Source/Atomic/Core/WorkQueue.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -90,6 +90,10 @@ public:
     SharedPtr<WorkItem> GetFreeItem();
     /// Add a work item and resume worker threads.
     void AddWorkItem(SharedPtr<WorkItem> item);
+    /// Remove a work item before it has started executing. Return true if successfully removed.
+    bool RemoveWorkItem(SharedPtr<WorkItem> item);
+    /// Remove a number of work items before they have started executing. Return the number of items successfully removed.
+    unsigned RemoveWorkItems(const Vector<SharedPtr<WorkItem> >& items);
     /// Pause worker threads.
     void Pause();
     /// Resume worker threads.
@@ -117,6 +121,8 @@ private:
     void PurgeCompleted(unsigned priority);
     /// Purge the pool to reduce allocation where its unneeded.
     void PurgePool();
+    /// Return a work item to the pool.
+    void ReturnToPool(SharedPtr<WorkItem>& item);
     /// Handle frame start event. Purge completed work from the main thread queue, and perform work if no threads at all.
     void HandleBeginFrame(StringHash eventType, VariantMap& eventData);
     

+ 5 - 7
Source/Atomic/Engine/DebugHud.cpp

@@ -1,7 +1,7 @@
 #ifdef __disabled
 
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -26,10 +26,12 @@
 #include "../Core/CoreEvents.h"
 #include "../Engine/DebugHud.h"
 #include "../Engine/Engine.h"
+#include "../UI/Font.h"
 #include "../Graphics/Graphics.h"
 #include "../IO/Log.h"
 #include "../Core/Profiler.h"
 #include "../Graphics/Renderer.h"
+#include "../UI/Text.h"
 #include "../UI/UI.h"
 
 #include "../DebugNew.h"
@@ -143,7 +145,7 @@ void DebugHud::Update()
     if (modeText_->IsVisible())
     {
         String mode;
-        mode.AppendWithFormat("Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s Mode:%s",
+        mode.AppendWithFormat("Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s API:%s",
             qualityTexts[renderer->GetTextureQuality()],
             qualityTexts[renderer->GetMaterialQuality()],
             renderer->GetSpecularLighting() ? "On" : "Off",
@@ -152,11 +154,7 @@ void DebugHud::Update()
             shadowQualityTexts[renderer->GetShadowQuality()],
             renderer->GetMaxOccluderTriangles() > 0 ? "On" : "Off",
             renderer->GetDynamicInstancing() ? "On" : "Off",
-            #ifdef ATOMIC_OPENGL
-            "OGL");
-            #else
-            graphics->GetSM3Support() ? "SM3" : "SM2");
-            #endif
+            graphics->GetApiName().CString());
 
         modeText_->SetText(mode);
     }

+ 16 - 6
Source/Atomic/Engine/Engine.cpp

@@ -58,10 +58,13 @@
 #ifdef ATOMIC_TBUI
 #include "../UI/UI.h"
 #endif
-
 #include "../Core/WorkQueue.h"
 #include "../Resource/XMLFile.h"
 
+#if defined(EMSCRIPTEN) && defined(URHO3D_TESTING)
+#include <emscripten.h>
+#endif
+
 #include "../DebugNew.h"
 
 #if defined(_MSC_VER) && defined(_DEBUG)
@@ -271,7 +274,7 @@ bool Engine::Initialize(const VariantMap& parameters)
         else
             LOGDEBUGF("Skip specified resource package '%s' as it does not exist, check the documentation on how to set the 'resource prefix path'", resourcePackages[i].CString());
     }
-    
+
     // Add auto load folders. Prioritize these (if exist) before the default folders
     for (unsigned i = 0; i < autoLoadPaths.Size(); ++i)
     {
@@ -327,7 +330,6 @@ bool Engine::Initialize(const VariantMap& parameters)
 
         if (HasParameter(parameters, "ExternalWindow"))
             graphics->SetExternalWindow(GetParameter(parameters, "ExternalWindow").GetVoidPtr());
-        graphics->SetForceSM2(GetParameter(parameters, "ForceSM2", false).GetBool());
         graphics->SetWindowTitle(GetParameter(parameters, "WindowTitle", "Atomic").GetString());
         graphics->SetWindowIcon(cache->GetResource<Image>(GetParameter(parameters, "WindowIcon", String::EMPTY).GetString()));
         graphics->SetFlushGPU(GetParameter(parameters, "FlushGPU", false).GetBool());
@@ -336,6 +338,11 @@ bool Engine::Initialize(const VariantMap& parameters)
         if (HasParameter(parameters, "WindowPositionX") && HasParameter(parameters, "WindowPositionY"))
             graphics->SetWindowPosition(GetParameter(parameters, "WindowPositionX").GetInt(), GetParameter(parameters, "WindowPositionY").GetInt());
 
+        #ifdef URHO3D_OPENGL
+        if (HasParameter(parameters, "ForceGL2"))
+            graphics->SetForceGL2(GetParameter(parameters, "ForceGL2").GetBool());
+        #endif
+
         if (!graphics->SetMode(
             GetParameter(parameters, "WindowWidth", 0).GetInt(),
             GetParameter(parameters, "WindowHeight", 0).GetInt(),
@@ -378,7 +385,7 @@ bool Engine::Initialize(const VariantMap& parameters)
         renderer->SetTextureQuality(GetParameter(parameters, "TextureQuality", QUALITY_HIGH).GetInt());
         renderer->SetTextureFilterMode((TextureFilterMode)GetParameter(parameters, "TextureFilterMode", FILTER_TRILINEAR).GetInt());
         renderer->SetTextureAnisotropy(GetParameter(parameters, "TextureAnisotropy", 4).GetInt());
-        
+
         if (GetParameter(parameters, "Sound", true).GetBool())
         {
             GetSubsystem<Audio>()->SetMode(
@@ -770,6 +777,8 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
                 ret["FrameLimiter"] = false;
             else if (argument == "flushgpu")
                 ret["FlushGPU"] = true;
+            else if (argument == "gl2")
+                ret["ForceGL2"] = true;
             else if (argument == "landscape")
                 ret["Orientations"] = "LandscapeLeft LandscapeRight " + ret["Orientations"].GetString();
             else if (argument == "portrait")
@@ -795,8 +804,6 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
                 ret["LowQualityShadows"] = true;
             else if (argument == "nothreads")
                 ret["WorkerThreads"] = false;
-            else if (argument == "sm2")
-                ret["ForceSM2"] = true;
             else if (argument == "v")
                 ret["VSync"] = true;
             else if (argument == "t")
@@ -934,6 +941,9 @@ void Engine::DoExit()
         graphics->Close();
 
     exiting_ = true;
+    #if defined(EMSCRIPTEN) && defined(URHO3D_TESTING)
+    emscripten_force_exit(EXIT_SUCCESS);    // Some how this is required to signal emrun to stop
+    #endif
 }
 
 }

+ 12 - 4
Source/Atomic/IO/File.cpp

@@ -125,7 +125,7 @@ bool File::Open(const String& fileName, FileMode mode)
     FileSystem* fileSystem = GetSubsystem<FileSystem>();
     if (fileSystem && !fileSystem->CheckAccess(GetPath(fileName)))
     {
-        LOGERROR("Access denied to " + fileName);
+        LOGERRORF("Access denied to %s", fileName.CString());
         return false;
     }
 
@@ -141,7 +141,7 @@ bool File::Open(const String& fileName, FileMode mode)
         assetHandle_ = SDL_RWFromFile(fileName.Substring(5).CString(), "rb");
         if (!assetHandle_)
         {
-            LOGERROR("Could not open asset file " + fileName);
+            LOGERRORF("Could not open asset file %s", fileName.CString());
             return false;
         }
         else
@@ -184,7 +184,7 @@ bool File::Open(const String& fileName, FileMode mode)
     
     if (!handle_)
     {
-        LOGERROR("Could not open file " + fileName);
+        LOGERRORF("Could not open file %s", fileName.CString());
         return false;
     }
 
@@ -198,8 +198,16 @@ bool File::Open(const String& fileName, FileMode mode)
     writeSyncNeeded_ = false;
 
     fseek((FILE*)handle_, 0, SEEK_END);
-    size_ = ftell((FILE*)handle_);
+    long size = ftell((FILE*)handle_);
     fseek((FILE*)handle_, 0, SEEK_SET);
+    if (size > M_MAX_UNSIGNED)
+    {
+        LOGERRORF("Could not open file %s which is larger than 4GB", fileName.CString());
+        Close();
+        size_ = 0;
+        return false;
+    }
+    size_ = (unsigned)size;
     return true;
 }
 

+ 6 - 6
Source/Atomic/IO/RWOpsWrapper.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -47,14 +47,14 @@ public:
 
     /// Return the RWOps structure.
     SDL_RWops* GetRWOps() { return &ops_; }
-    
+
 private:
     /// Return data size of the object.
     static Sint64 Size(SDL_RWops* context)
     {
         T* object = reinterpret_cast<T*>(context->hidden.unknown.data1);
         Deserializer* des = dynamic_cast<Deserializer*>(object);
-        return des ? des->GetSize() : 0;
+        return des ? (Sint64)des->GetSize() : 0;
     }
 
     /// Seek within the object's data.
@@ -74,7 +74,7 @@ private:
         case RW_SEEK_CUR:
             des->Seek((unsigned)(des->GetPosition() + offset));
             break;
-            
+
         case RW_SEEK_END:
             des->Seek((unsigned)(des->GetSize() + offset));
             break;
@@ -98,7 +98,7 @@ private:
     {
         T* object = reinterpret_cast<T*>(context->hidden.unknown.data1);
         Deserializer* des = dynamic_cast<Deserializer*>(object);
-        return des ? des->Read(ptr, size * maxNum) / size : 0;
+        return des ? (size_t)(des->Read(ptr, size * maxNum) / size) : 0;
     }
 
     /// Write to the object. Return number of "packets" written.
@@ -106,7 +106,7 @@ private:
     {
         T* object = reinterpret_cast<T*>(context->hidden.unknown.data1);
         Serializer* ser = dynamic_cast<Serializer*>(object);
-        return ser ? ser->Write(ptr, size * maxNum) / size : 0;
+        return ser ? (size_t)(ser->Write(ptr, size * maxNum) / size) : 0;
     }
 
     /// SDL RWOps structure associated with the object.

+ 91 - 78
Source/Atomic/Input/Input.cpp

@@ -46,10 +46,9 @@
 
 extern "C" int SDL_AddTouch(SDL_TouchID touchID, const char *name);
 
-// Require a click inside window before re-hiding mouse cursor on OSX, otherwise dragging the window
-// can be incorrectly interpreted as mouse movement inside the window
-#if defined(__APPLE__) && !defined(IOS)
-    #define REQUIRE_CLICK_TO_FOCUS
+// Use a "click inside window to focus" mechanism on desktop platforms when the mouse cursor is hidden
+#if defined(WIN32) || (defined(__APPLE__) && !defined(IOS)) || (defined(__linux__) && !defined(ANDROID) && !defined(RPI))
+#define REQUIRE_CLICK_TO_FOCUS
 #endif
 
 namespace Atomic
@@ -204,6 +203,7 @@ Input::Input(Context* context) :
     Object(context),
     mouseButtonDown_(0),
     mouseButtonPress_(0),
+    lastVisibleMousePosition_(MOUSE_POSITION_OFFSCREEN),
     mouseMoveWheel_(0),
     windowID_(0),
     toggleFullscreen_(true),
@@ -211,16 +211,17 @@ Input::Input(Context* context) :
     lastMouseVisible_(false),
     mouseGrabbed_(false),
     mouseMode_(MM_ABSOLUTE),
-#ifdef EMSCRIPTEN
+    #ifdef EMSCRIPTEN
     emscriptenExitingPointerLock_(false),
     emscriptenEnteredPointerLock_(false),
-#endif
-    lastVisibleMousePosition_(MOUSE_POSITION_OFFSCREEN),
+    #endif
     touchEmulation_(false),
     inputFocus_(false),
     minimized_(false),
     focusedThisFrame_(false),
     suppressNextMouseMove_(false),
+    inResize_(false),
+    screenModeChanged_(false),
     initialized_(false)
 {
     for (int i = 0; i < TOUCHID_MAX; i++)
@@ -270,15 +271,6 @@ void Input::Update()
         state.delta_ = IntVector2::ZERO;
     }
 
-    // Check and handle SDL events
-
-    if (!inputFocus_)
-    {
-        // While there is no input focus, don't process key, mouse, touch or joystick events.
-        SDL_PumpEvents();
-        SDL_FlushEvents(SDL_KEYDOWN, SDL_MULTIGESTURE);
-    }
-
     SDL_Event evt;
     while (SDL_PollEvent(&evt))
         HandleSDLEvent(&evt);
@@ -286,16 +278,19 @@ void Input::Update()
     // Check for focus change this frame
     SDL_Window* window = graphics_->GetImpl()->GetWindow();
     unsigned flags = window ? SDL_GetWindowFlags(window) & (SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS) : 0;
-#ifndef EMSCRIPTEN
+    #ifndef EMSCRIPTEN
     if (window)
     {
-#if defined(REQUIRE_CLICK_TO_FOCUS)
-        if (!inputFocus_ && (graphics_->GetFullscreen() || mouseVisible_) && flags == (SDL_WINDOW_INPUT_FOCUS |
-            SDL_WINDOW_MOUSE_FOCUS))
-#else
+        #ifdef REQUIRE_CLICK_TO_FOCUS
+        // When using the "click to focus" mechanism, only focus automatically in fullscreen or non-hidden mouse mode
+        if (!inputFocus_ && (mouseVisible_ || graphics_->GetFullscreen() || screenModeChanged_) && (flags & SDL_WINDOW_INPUT_FOCUS))
+        #else
         if (!inputFocus_ && (flags & SDL_WINDOW_INPUT_FOCUS))
-#endif
+        #endif
+        {
+            screenModeChanged_ = false;
             focusedThisFrame_ = true;
+        }
 
         if (focusedThisFrame_)
             GainFocus();
@@ -363,7 +358,12 @@ void Input::Update()
     #endif
 
     // Check for relative mode mouse move
+    // Note that Emscripten will use SDL mouse move events for relative mode instead
+    #ifndef EMSCRIPTEN
+    if (!touchEmulation_ && (graphics_->GetExternalWindow() || (!mouseVisible_ && inputFocus_ && (flags & SDL_WINDOW_MOUSE_FOCUS))))
+    #else
     if (!touchEmulation_ && mouseMode_ != MM_RELATIVE && (graphics_->GetExternalWindow() || (!mouseVisible_ && inputFocus_ && (flags & SDL_WINDOW_MOUSE_FOCUS))))
+    #endif
     {
         IntVector2 mousePosition = GetMousePosition();
         mouseMove_ = mousePosition - lastMousePosition_;
@@ -382,9 +382,7 @@ void Input::Update()
         }
         #else
         if (mouseMode_ == MM_ABSOLUTE)
-        {
             lastMousePosition_ = mousePosition;
-        }
         #endif
         // Send mouse move event if necessary
         if (mouseMove_ != IntVector2::ZERO)
@@ -412,19 +410,6 @@ void Input::Update()
             }
         }
     }
-
-    #ifndef EMSCRIPTEN
-    if (mouseMode_ == MM_RELATIVE)
-    {
-        IntVector2 mousePosition = GetMousePosition();
-        IntVector2 center(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2);
-        if (mousePosition != center)
-        {
-            SetMousePosition(center);
-            lastMousePosition_ = center;
-        }
-    }
-    #endif
 }
 
 void Input::SetMouseVisible(bool enable, bool suppressEvent)
@@ -566,9 +551,7 @@ void Input::SetMouseMode(MouseMode mode)
         }
         #ifndef EMSCRIPTEN
         else if (previousMode == MM_WRAP)
-        {
             SDL_SetWindowGrab(window, SDL_FALSE);
-        }
         #endif
 
         // Handle changing to new mode
@@ -935,18 +918,18 @@ unsigned Input::LoadGestures(Deserializer& source)
 
 bool Input::RemoveGesture(unsigned gestureID)
 {
-#if defined(EMSCRIPTEN)
+    #ifdef EMSCRIPTEN
     return false;
-#else
+    #else
     return SDL_RemoveDollarTemplate(gestureID) != 0;
-#endif
+    #endif
 }
 
 void Input::RemoveAllGestures()
 {
-#if !defined(EMSCRIPTEN)
+    #ifndef EMSCRIPTEN
     SDL_RemoveAllDollarTemplates();
-#endif
+    #endif
 }
 
 SDL_JoystickID Input::OpenJoystick(unsigned index)
@@ -1168,15 +1151,15 @@ void Input::Initialize()
 
     // Set the initial activation
     initialized_ = true;
-#ifndef EMSCRIPTEN
+    #ifndef EMSCRIPTEN
     focusedThisFrame_ = true;
-#else
+    #else
     // Note: Page visibility and focus are slightly different, however we can't query last focus with Emscripten (1.29.0)
     if (emscriptenInput_->IsVisible())
         GainFocus();
     else
         LoseFocus();
-#endif
+    #endif
 
     ResetJoysticks();
     ResetState();
@@ -1355,15 +1338,7 @@ void Input::SendInputFocusEvent()
 
 void Input::SetMouseButton(int button, bool newState)
 {
-#ifdef REQUIRE_CLICK_TO_FOCUS
-    if (!mouseVisible_ && !graphics_->GetFullscreen())
-    {
-        if (!inputFocus_ && newState && button == MOUSEB_LEFT)
-            focusedThisFrame_ = true;
-    }
-#endif
-
-#ifdef EMSCRIPTEN
+    #ifdef EMSCRIPTEN
     if (emscriptenEnteredPointerLock_)
     {
         // Suppress mouse jump on initial Pointer Lock
@@ -1373,11 +1348,7 @@ void Input::SetMouseButton(int button, bool newState)
         suppressNextMouseMove_ = true;
         emscriptenEnteredPointerLock_ = false;
     }
-#endif
-
-    // If we do not have focus yet, do not react to the mouse button down
-    if (!graphics_->GetExternalWindow() && newState && !inputFocus_)
-        return;
+    #endif
 
     if (newState)
     {
@@ -1405,10 +1376,6 @@ void Input::SetMouseButton(int button, bool newState)
 
 void Input::SetKey(int key, int scancode, unsigned raw, bool newState)
 {
-    // If we do not have focus yet, do not react to the key down
-    if (!graphics_->GetExternalWindow() && newState && !inputFocus_)
-        return;
-
     bool repeat = false;
 
     if (newState)
@@ -1451,10 +1418,6 @@ void Input::SetKey(int key, int scancode, unsigned raw, bool newState)
 
 void Input::SetMouseWheel(int delta)
 {
-    // If we do not have focus yet, do not react to the wheel
-    if (!graphics_->GetExternalWindow() && !inputFocus_)
-        return;
-
     if (delta)
     {
         mouseMoveWheel_ += delta;
@@ -1481,23 +1444,46 @@ void Input::HandleSDLEvent(void* sdlEvent)
 {
     SDL_Event& evt = *static_cast<SDL_Event*>(sdlEvent);
 
+    // While not having input focus, skip key/mouse/touch/joystick events, except for the "click to focus" mechanism
+    if (!inputFocus_ && evt.type >= SDL_KEYDOWN && evt.type <= SDL_MULTIGESTURE)
+    {
+        #ifdef REQUIRE_CLICK_TO_FOCUS
+        // Require the click to be at least 1 pixel inside the window to disregard clicks in the title bar
+        if (evt.type == SDL_MOUSEBUTTONDOWN && evt.button.x > 0 && evt.button.y > 0 && evt.button.x < graphics_->GetWidth() - 1 &&
+            evt.button.y < graphics_->GetHeight() - 1)
+        {
+            focusedThisFrame_ = true;
+            // Do not cause the click to actually go throughfin
+            return;
+        }
+        else if (evt.type == SDL_FINGERDOWN)
+        {
+            // When focusing by touch, call GainFocus() immediately as it resets the state; a touch has sustained state
+            // which should be kept
+            GainFocus();
+        }
+        else
+        #endif
+            return;
+    }
+
     switch (evt.type)
     {
     case SDL_KEYDOWN:
         // Convert to uppercase to match Win32 virtual key codes
-#if defined (EMSCRIPTEN)
+        #ifdef EMSCRIPTEN
         SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, 0, true);
-#else
+        #else
         SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, evt.key.keysym.raw, true);
-#endif
+        #endif
         break;
 
     case SDL_KEYUP:
-#if defined(EMSCRIPTEN)
+        #ifdef EMSCRIPTEN
         SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, 0, false);
-#else
+        #else
         SetKey(ConvertSDLKeyCode(evt.key.keysym.sym, evt.key.keysym.scancode), evt.key.keysym.scancode, evt.key.keysym.raw, false);
-#endif
+        #endif
         break;
 
     case SDL_TEXTINPUT:
@@ -1561,7 +1547,13 @@ void Input::HandleSDLEvent(void* sdlEvent)
         break;
 
     case SDL_MOUSEMOTION:
+        // Emscripten will use SDL mouse move events for relative mode, as repositioning the mouse and
+        // measuring distance from window center is not supported
+        #ifndef EMSCRIPTEN
+        if (mouseVisible_ && !touchEmulation_)
+        #else
         if ((mouseVisible_ || mouseMode_ == MM_RELATIVE) && !touchEmulation_)
+        #endif
         {
             mouseMove_.x_ += evt.motion.xrel;
             mouseMove_.y_ += evt.motion.yrel;
@@ -1627,6 +1619,10 @@ void Input::HandleSDLEvent(void* sdlEvent)
             eventData[P_Y] = state.position_.y_;
             eventData[P_PRESSURE] = state.pressure_;
             SendEvent(E_TOUCHBEGIN, eventData);
+
+            // Finger touch may move the mouse cursor. Suppress next mouse move when cursor hidden to prevent jumps
+            if (!mouseVisible_)
+                suppressNextMouseMove_ = true;
         }
         break;
 
@@ -1686,6 +1682,10 @@ void Input::HandleSDLEvent(void* sdlEvent)
             #endif
             eventData[P_PRESSURE] = state.pressure_;
             SendEvent(E_TOUCHMOVE, eventData);
+
+            // Finger touch may move the mouse cursor. Suppress next mouse move when cursor hidden to prevent jumps
+            if (!mouseVisible_)
+                suppressNextMouseMove_ = true;
         }
         break;
 
@@ -1935,7 +1935,9 @@ void Input::HandleSDLEvent(void* sdlEvent)
             #endif
 
             case SDL_WINDOWEVENT_RESIZED:
+                inResize_ = true;
                 graphics_->WindowResized();
+                inResize_ = false;
                 break;
             case SDL_WINDOWEVENT_MOVED:
                 graphics_->WindowMoved();
@@ -1975,17 +1977,28 @@ void Input::HandleScreenMode(StringHash eventType, VariantMap& eventData)
     SDL_Window* window = graphics_->GetImpl()->GetWindow();
     windowID_ = SDL_GetWindowID(window);
 
-    if (!mouseVisible_)
+    // If screen mode happens due to mouse drag resize, do not recenter the mouse as that would lead to erratic window sizes
+    if (!mouseVisible_ && !inResize_)
     {
         IntVector2 center(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2);
         SetMousePosition(center);
         lastMousePosition_ = center;
+        focusedThisFrame_ = true;
+    }
+    else
+        lastMousePosition_ = GetMousePosition();
+
     }
-    
-    focusedThisFrame_ = true;
+
+    if (graphics_->GetFullscreen())
+        focusedThisFrame_ = true;
 
     // After setting a new screen mode we should not be minimized
     minimized_ = (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0;
+
+    // Remember that screen mode changed in case we lose focus (needed on Linux)
+    if (!inResize_)
+        screenModeChanged_ = true;
 }
 
 void Input::HandleBeginFrame(StringHash eventType, VariantMap& eventData)

+ 4 - 0
Source/Atomic/Input/Input.h

@@ -364,6 +364,10 @@ private:
     bool focusedThisFrame_;
     /// Next mouse move suppress flag.
     bool suppressNextMouseMove_;
+    /// Handling a window resize event flag.
+    bool inResize_;
+    /// Flag for automatic focus (without click inside window) after screen mode change, needed on Linux.
+    bool screenModeChanged_;
     /// Initialized flag.
     bool initialized_;
 #ifdef EMSCRIPTEN

+ 166 - 115
Source/Atomic/Navigation/NavigationMesh.cpp

@@ -31,8 +31,13 @@
 #include "../IO/Log.h"
 #include "../IO/MemoryBuffer.h"
 #include "../Atomic3D/Model.h"
+#include "../Navigation/DynamicNavigationMesh.h"
+#include "../Navigation/NavArea.h"
+#include "../Navigation/NavBuildData.h"
 #include "../Navigation/Navigable.h"
+#include "../Navigation/NavigationEvents.h"
 #include "../Navigation/NavigationMesh.h"
+#include "../Navigation/Obstacle.h"
 #include "../Navigation/OffMeshConnection.h"
 #include "../Core/Profiler.h"
 #include "../Scene/Scene.h"
@@ -46,11 +51,21 @@
 #include <Detour/include/DetourNavMeshQuery.h>
 #include <Recast/include/Recast.h>
 
+#include "../Navigation/CrowdAgent.h"
+#include "../Navigation/DetourCrowdManager.h"
+
 #include "../DebugNew.h"
 
 namespace Atomic
 {
 
+const char* navmeshPartitionTypeNames[] =
+{
+    "watershed",
+    "monotone",
+    0
+};
+
 const char* NAVIGATION_CATEGORY = "Navigation";
 
 static const int DEFAULT_TILE_SIZE = 128;
@@ -69,67 +84,6 @@ static const float DEFAULT_DETAIL_SAMPLE_MAX_ERROR = 1.0f;
 
 static const int MAX_POLYS = 2048;
 
-/// Temporary data for building one tile of the navigation mesh.
-struct NavigationBuildData
-{
-    /// Construct.
-    NavigationBuildData() :
-        ctx_(new rcContext(false)),
-        heightField_(0),
-        compactHeightField_(0),
-        contourSet_(0),
-        polyMesh_(0),
-        polyMeshDetail_(0)
-    {
-    }
-
-    /// Destruct.
-    ~NavigationBuildData()
-    {
-        delete(ctx_);
-        rcFreeHeightField(heightField_);
-        rcFreeCompactHeightfield(compactHeightField_);
-        rcFreeContourSet(contourSet_);
-        rcFreePolyMesh(polyMesh_);
-        rcFreePolyMeshDetail(polyMeshDetail_);
-
-        ctx_ = 0;
-        heightField_ = 0;
-        compactHeightField_ = 0;
-        contourSet_ = 0;
-        polyMesh_ = 0;
-        polyMeshDetail_ = 0;
-    }
-
-    /// World-space bounding box of the navigation mesh tile.
-    BoundingBox worldBoundingBox_;
-    /// Vertices from geometries.
-    PODVector<Vector3> vertices_;
-    /// Triangle indices from geometries.
-    PODVector<int> indices_;
-    /// Offmesh connection vertices.
-    PODVector<Vector3> offMeshVertices_;
-    /// Offmesh connection radii.
-    PODVector<float> offMeshRadii_;
-    /// Offmesh connection flags.
-    PODVector<unsigned short> offMeshFlags_;
-    /// Offmesh connection areas.
-    PODVector<unsigned char> offMeshAreas_;
-    /// Offmesh connection direction.
-    PODVector<unsigned char> offMeshDir_;
-    /// Recast context.
-    rcContext* ctx_;
-    /// Recast heightfield.
-    rcHeightfield* heightField_;
-    /// Recast compact heightfield.
-    rcCompactHeightfield* compactHeightField_;
-    /// Recast contour set.
-    rcContourSet* contourSet_;
-    /// Recast poly mesh.
-    rcPolyMesh* polyMesh_;
-    /// Recast detail poly mesh.
-    rcPolyMeshDetail* polyMeshDetail_;
-};
 
 /// Temporary data for finding a path.
 struct FindPathData
@@ -142,6 +96,8 @@ struct FindPathData
     Vector3 pathPoints_[MAX_POLYS];
     // Flags on the path.
     unsigned char pathFlags_[MAX_POLYS];
+    // Area Ids on the path.
+    unsigned char pathAreras_[MAX_POLYS];
 };
 
 NavigationMesh::NavigationMesh(Context* context) :
@@ -165,7 +121,9 @@ NavigationMesh::NavigationMesh(Context* context) :
     detailSampleMaxError_(DEFAULT_DETAIL_SAMPLE_MAX_ERROR),
     padding_(Vector3::ONE),
     numTilesX_(0),
-    numTilesZ_(0)
+    numTilesZ_(0),
+    partitionType_(NAVMESH_PARTITION_WATERSHED),
+    keepInterResults_(false)
 {
 }
 
@@ -199,6 +157,7 @@ void NavigationMesh::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE("Detail Sample Max Error", GetDetailSampleMaxError, SetDetailSampleMaxError, float, DEFAULT_DETAIL_SAMPLE_MAX_ERROR, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE("Bounding Box Padding", GetPadding, SetPadding, Vector3, Vector3::ONE, AM_DEFAULT);
     MIXED_ACCESSOR_ATTRIBUTE("Navigation Data", GetNavigationDataAttr, SetNavigationDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
+    ENUM_ACCESSOR_ATTRIBUTE("Partition Type", GetPartitionType, SetPartitionType, NavmeshPartitionType, navmeshPartitionTypeNames, NAVMESH_PARTITION_WATERSHED, AM_DEFAULT);
 }
 
 void NavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
@@ -214,27 +173,35 @@ void NavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
     {
         for (int x = 0; x < numTilesX_; ++x)
         {
-            const dtMeshTile* tile = navMesh->getTileAt(x, z, 0);
-            if (!tile)
-                continue;
-
-            for (int i = 0; i < tile->header->polyCount; ++i)
+            for (int i = 0; i < 128; ++i)
             {
-                dtPoly* poly = tile->polys + i;
-                for (unsigned j = 0; j < poly->vertCount; ++j)
+                const dtMeshTile* tile = navMesh->getTileAt(x, z, i);
+                if (!tile)
+                    continue;
+
+                for (int i = 0; i < tile->header->polyCount; ++i)
                 {
-                    debug->AddLine(
-                        worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[j] * 3]),
-                        worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[(j + 1) % poly->vertCount] * 3]),
-                        Color::YELLOW,
-                        depthTest
-                    );
+                    dtPoly* poly = tile->polys + i;
+                    for (unsigned j = 0; j < poly->vertCount; ++j)
+                    {
+                        debug->AddLine(
+                            worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[j] * 3]),
+                            worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[(j + 1) % poly->vertCount] * 3]),
+                            Color::YELLOW,
+                            depthTest
+                            );
+                    }
                 }
             }
         }
     }
 }
 
+void NavigationMesh::SetMeshName(const String& newName)
+{
+    meshName_ = newName;
+}
+
 void NavigationMesh::SetTileSize(int size)
 {
     tileSize_ = Max(size, 16);
@@ -416,6 +383,16 @@ bool NavigationMesh::Build()
         }
 
         LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles");
+
+        // Send a notification event to concerned parties that we've been fully rebuilt
+        {
+            using namespace NavigationMeshRebuilt;
+            VariantMap& buildEventParams = GetContext()->GetEventDataMap();
+            buildEventParams[P_NODE] = node_;
+            buildEventParams[P_MESH] = this;
+            SendEvent(E_NAVIGATION_MESH_REBUILT, buildEventParams);
+        }
+
         return true;
     }
 }
@@ -522,7 +499,7 @@ void NavigationMesh::FindPath(PODVector<Vector3>& dest, const Vector3& start, co
 
     Vector3 localStart = inverse * start;
     Vector3 localEnd = inverse * end;
-
+    
     dtPolyRef startRef;
     dtPolyRef endRef;
     navMeshQuery_->findNearestPoly(&localStart.x_, &extents.x_, queryFilter_, &startRef, 0);
@@ -648,11 +625,24 @@ void NavigationMesh::DrawDebugGeometry(bool depthTest)
     }
 }
 
+void NavigationMesh::SetAreaCost(unsigned areaID, float cost)
+{
+    if (queryFilter_)
+        queryFilter_->setAreaCost((int)areaID, cost);
+}
+
 BoundingBox NavigationMesh::GetWorldBoundingBox() const
 {
     return node_ ? boundingBox_.Transformed(node_->GetWorldTransform()) : boundingBox_;
 }
 
+float NavigationMesh::GetAreaCost(unsigned areaID) const
+{
+    if (queryFilter_)
+        return queryFilter_->getAreaCost((int)areaID);
+    return 1.0f;
+}
+
 void NavigationMesh::SetNavigationDataAttr(const PODVector<unsigned char>& value)
 {
     ReleaseNavigationMesh();
@@ -790,6 +780,22 @@ void NavigationMesh::CollectGeometries(Vector<NavigationGeometryInfo>& geometryL
             geometryList.Push(info);
         }
     }
+
+    // Get nav area volumes
+    PODVector<NavArea*> navAreas;
+    node_->GetComponents<NavArea>(navAreas, true);
+    for (unsigned i = 0; i < navAreas.Size(); ++i)
+    {
+        NavArea* area = navAreas[i];
+        // Ignore disabled AND any areas that have no meaningful settings
+        if (area->IsEnabledEffective() && area->GetAreaID() != 0)
+        {
+            NavigationGeometryInfo info;
+            info.component_ = area;
+            info.boundingBox_ = area->GetWorldBoundingBox();
+            geometryList.Push(info);
+        }
+    }
 }
 
 void NavigationMesh::CollectGeometries(Vector<NavigationGeometryInfo>& geometryList, Node* node, HashSet<Node*>& processedNodes, bool recursive)
@@ -866,7 +872,7 @@ void NavigationMesh::CollectGeometries(Vector<NavigationGeometryInfo>& geometryL
     }
 }
 
-void NavigationMesh::GetTileGeometry(NavigationBuildData& build, Vector<NavigationGeometryInfo>& geometryList, BoundingBox& box)
+void NavigationMesh::GetTileGeometry(NavBuildData* build, Vector<NavigationGeometryInfo>& geometryList, BoundingBox& box)
 {
     Matrix3x4 inverse = node_->GetWorldTransform().Inverse();
 
@@ -882,14 +888,21 @@ void NavigationMesh::GetTileGeometry(NavigationBuildData& build, Vector<Navigati
                 Vector3 start = inverse * connection->GetNode()->GetWorldPosition();
                 Vector3 end = inverse * connection->GetEndPoint()->GetWorldPosition();
 
-                build.offMeshVertices_.Push(start);
-                build.offMeshVertices_.Push(end);
-                build.offMeshRadii_.Push(connection->GetRadius());
-                /// \todo Allow to define custom flags
-                build.offMeshFlags_.Push(0x1);
-
-                build.offMeshAreas_.Push(0);
-                build.offMeshDir_.Push(connection->IsBidirectional() ? DT_OFFMESH_CON_BIDIR : 0);
+                build->offMeshVertices_.Push(start);
+                build->offMeshVertices_.Push(end);
+                build->offMeshRadii_.Push(connection->GetRadius());
+                build->offMeshFlags_.Push(connection->GetMask());
+                build->offMeshAreas_.Push((unsigned char)connection->GetAreaID());
+                build->offMeshDir_.Push(connection->IsBidirectional() ? DT_OFFMESH_CON_BIDIR : 0);
+                continue;
+            }
+            else if (geometryList[i].component_->GetType() == NavArea::GetTypeStatic())
+            {
+                NavArea* area = static_cast<NavArea*>(geometryList[i].component_);
+                NavAreaStub stub;
+                stub.areaID_ = (unsigned char)area->GetAreaID();
+                stub.bounds_ = area->GetWorldBoundingBox();
+                build->navAreas_.Push(stub);
                 continue;
             }
 
@@ -919,28 +932,28 @@ void NavigationMesh::GetTileGeometry(NavigationBuildData& build, Vector<Navigati
 
                         unsigned numVertices = data->vertexCount_;
                         unsigned numIndices = data->indexCount_;
-                        unsigned destVertexStart = build.vertices_.Size();
+                        unsigned destVertexStart = build->vertices_.Size();
 
                         for (unsigned j = 0; j < numVertices; ++j)
-                            build.vertices_.Push(transform * data->vertexData_[j]);
+                            build->vertices_.Push(transform * data->vertexData_[j]);
 
                         for (unsigned j = 0; j < numIndices; ++j)
-                            build.indices_.Push(data->indexData_[j] + destVertexStart);
+                            build->indices_.Push(data->indexData_[j] + destVertexStart);
                     }
                     break;
 
                 case SHAPE_BOX:
                     {
-                        unsigned destVertexStart = build.vertices_.Size();
+                        unsigned destVertexStart = build->vertices_.Size();
 
-                        build.vertices_.Push(transform * Vector3(-0.5f, 0.5f, -0.5f));
-                        build.vertices_.Push(transform * Vector3(0.5f, 0.5f, -0.5f));
-                        build.vertices_.Push(transform * Vector3(0.5f, -0.5f, -0.5f));
-                        build.vertices_.Push(transform * Vector3(-0.5f, -0.5f, -0.5f));
-                        build.vertices_.Push(transform * Vector3(-0.5f, 0.5f, 0.5f));
-                        build.vertices_.Push(transform * Vector3(0.5f, 0.5f, 0.5f));
-                        build.vertices_.Push(transform * Vector3(0.5f, -0.5f, 0.5f));
-                        build.vertices_.Push(transform * Vector3(-0.5f, -0.5f, 0.5f));
+                        build->vertices_.Push(transform * Vector3(-0.5f, 0.5f, -0.5f));
+                        build->vertices_.Push(transform * Vector3(0.5f, 0.5f, -0.5f));
+                        build->vertices_.Push(transform * Vector3(0.5f, -0.5f, -0.5f));
+                        build->vertices_.Push(transform * Vector3(-0.5f, -0.5f, -0.5f));
+                        build->vertices_.Push(transform * Vector3(-0.5f, 0.5f, 0.5f));
+                        build->vertices_.Push(transform * Vector3(0.5f, 0.5f, 0.5f));
+                        build->vertices_.Push(transform * Vector3(0.5f, -0.5f, 0.5f));
+                        build->vertices_.Push(transform * Vector3(-0.5f, -0.5f, 0.5f));
 
                         const unsigned indices[] = {
                             0, 1, 2, 0, 2, 3, 1, 5, 6, 1, 6, 2, 4, 5, 1, 4, 1, 0, 5, 4, 7, 5, 7, 6,
@@ -948,7 +961,7 @@ void NavigationMesh::GetTileGeometry(NavigationBuildData& build, Vector<Navigati
                         };
 
                         for (unsigned j = 0; j < 36; ++j)
-                            build.indices_.Push(indices[j] + destVertexStart);
+                            build->indices_.Push(indices[j] + destVertexStart);
                     }
                     break;
 
@@ -971,7 +984,7 @@ void NavigationMesh::GetTileGeometry(NavigationBuildData& build, Vector<Navigati
     }
 }
 
-void NavigationMesh::AddTriMeshGeometry(NavigationBuildData& build, Geometry* geometry, const Matrix3x4& transform)
+void NavigationMesh::AddTriMeshGeometry(NavBuildData* build, Geometry* geometry, const Matrix3x4& transform)
 {
     if (!geometry)
         return;
@@ -994,12 +1007,12 @@ void NavigationMesh::AddTriMeshGeometry(NavigationBuildData& build, Geometry* ge
     if (!srcIndexCount)
         return;
 
-    unsigned destVertexStart = build.vertices_.Size();
+    unsigned destVertexStart = build->vertices_.Size();
 
     for (unsigned k = srcVertexStart; k < srcVertexStart + srcVertexCount; ++k)
     {
         Vector3 vertex = transform * *((const Vector3*)(&vertexData[k * vertexSize]));
-        build.vertices_.Push(vertex);
+        build->vertices_.Push(vertex);
     }
 
     // Copy remapped indices
@@ -1010,7 +1023,7 @@ void NavigationMesh::AddTriMeshGeometry(NavigationBuildData& build, Geometry* ge
 
         while (indices < indicesEnd)
         {
-            build.indices_.Push(*indices - srcVertexStart + destVertexStart);
+            build->indices_.Push(*indices - srcVertexStart + destVertexStart);
             ++indices;
         }
     }
@@ -1021,7 +1034,7 @@ void NavigationMesh::AddTriMeshGeometry(NavigationBuildData& build, Geometry* ge
 
         while (indices < indicesEnd)
         {
-            build.indices_.Push(*indices - srcVertexStart + destVertexStart);
+            build->indices_.Push(*indices - srcVertexStart + destVertexStart);
             ++indices;
         }
     }
@@ -1047,7 +1060,7 @@ bool NavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int
         boundingBox_.min_.z_ + tileEdgeLength * (float)(z + 1)
     ));
 
-    NavigationBuildData build;
+    SimpleNavBuildData build;
 
     rcConfig cfg;
     memset(&cfg, 0, sizeof cfg);
@@ -1077,7 +1090,7 @@ bool NavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int
     cfg.bmax[2] += cfg.borderSize * cfg.cs;
 
     BoundingBox expandedBox(*reinterpret_cast<Vector3*>(cfg.bmin), *reinterpret_cast<Vector3*>(cfg.bmax));
-    GetTileGeometry(build, geometryList, expandedBox);
+    GetTileGeometry(&build, geometryList, expandedBox);
 
     if (build.vertices_.Empty() || build.indices_.Empty())
         return true; // Nothing to do
@@ -1105,8 +1118,9 @@ bool NavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int
     rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
         triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
     rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
-    rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
+    
     rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
+    rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
 
     build.compactHeightField_ = rcAllocCompactHeightfield();
     if (!build.compactHeightField_)
@@ -1125,16 +1139,32 @@ bool NavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int
         LOGERROR("Could not erode compact heightfield");
         return false;
     }
-    if (!rcBuildDistanceField(build.ctx_, *build.compactHeightField_))
+
+    // Mark area volumes
+    for (unsigned i = 0; i < build.navAreas_.Size(); ++i)
+        rcMarkBoxArea(build.ctx_, &build.navAreas_[i].bounds_.min_.x_, &build.navAreas_[i].bounds_.max_.x_, build.navAreas_[i].areaID_, *build.compactHeightField_);
+
+    if (this->partitionType_ == NAVMESH_PARTITION_WATERSHED)
     {
-        LOGERROR("Could not build distance field");
-        return false;
+        if (!rcBuildDistanceField(build.ctx_, *build.compactHeightField_))
+        {
+            LOGERROR("Could not build distance field");
+            return false;
+        }
+        if (!rcBuildRegions(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea,
+            cfg.mergeRegionArea))
+        {
+            LOGERROR("Could not build regions");
+            return false;
+        }
     }
-    if (!rcBuildRegions(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea,
-        cfg.mergeRegionArea))
+    else
     {
-        LOGERROR("Could not build regions");
-        return false;
+        if (!rcBuildRegionsMonotone(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea))
+        {
+            LOGERROR("Could not build monotone regions");
+            return false;
+        }
     }
 
     build.contourSet_ = rcAllocContourSet();
@@ -1176,10 +1206,10 @@ bool NavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int
     }
 
     // Set polygon flags
-    /// \todo Allow to define custom flags
+    /// \todo Assignment of flags from navigation areas?
     for (int i = 0; i < build.polyMesh_->npolys; ++i)
     {
-        if (build.polyMesh_->areas[i] == RC_WALKABLE_AREA)
+        if (build.polyMesh_->areas[i] != RC_NULL_AREA)
             build.polyMesh_->flags[i] = 0x1;
     }
 
@@ -1235,6 +1265,16 @@ bool NavigationMesh::BuildTile(Vector<NavigationGeometryInfo>& geometryList, int
         return false;
     }
 
+    // Send a notification of the rebuild of this tile to anyone interested
+    {
+        using namespace NavigationAreaRebuilt;
+        VariantMap& eventData = GetContext()->GetEventDataMap();
+        eventData[P_NODE] = GetNode();
+        eventData[P_MESH] = this;
+        eventData[P_BOUNDSMIN] = Variant(tileBoundingBox.min_);
+        eventData[P_BOUNDSMAX] = Variant(tileBoundingBox.max_);
+        SendEvent(E_NAVIGATION_AREA_REBUILT, eventData);
+    }
     return true;
 }
 
@@ -1276,11 +1316,22 @@ void NavigationMesh::ReleaseNavigationMesh()
     boundingBox_.defined_ = false;
 }
 
+void NavigationMesh::SetPartitionType(NavmeshPartitionType ptype)
+{
+    partitionType_ = ptype;
+    MarkNetworkUpdate();
+}
+
 void RegisterNavigationLibrary(Context* context)
 {
     Navigable::RegisterObject(context);
     NavigationMesh::RegisterObject(context);
     OffMeshConnection::RegisterObject(context);
+    CrowdAgent::RegisterObject(context);
+    DetourCrowdManager::RegisterObject(context);
+    DynamicNavigationMesh::RegisterObject(context);
+    Obstacle::RegisterObject(context);
+    NavArea::RegisterObject(context);
 }
 
 }

+ 48 - 12
Source/Atomic/Navigation/NavigationMesh.h

@@ -31,14 +31,28 @@
 class dtNavMesh;
 class dtNavMeshQuery;
 class dtQueryFilter;
+struct dtNavMeshCreateParams;
+class rcContext;
+struct rcHeightfield;
+struct rcCompactHeightfield;
+struct rcContourSet;
+struct rcPolyMesh;
+struct rcPolyMeshDetail;
+struct rcHeightFieldLayerSet;
 
 namespace Atomic
 {
 
+enum NavmeshPartitionType
+{
+    NAVMESH_PARTITION_WATERSHED,
+    NAVMESH_PARTITION_MONOTONE,
+};
+
 class Geometry;
 
 struct FindPathData;
-struct NavigationBuildData;
+struct NavBuildData;
 
 /// Description of a navigation mesh geometry component, with transform and bounds information.
 struct NavigationGeometryInfo
@@ -54,9 +68,9 @@ struct NavigationGeometryInfo
 };
 
 /// Navigation mesh component. Collects the navigation geometry from child nodes with the Navigable component and responds to path queries.
-class ATOMIC_API NavigationMesh : public Component
 {
     OBJECT(NavigationMesh);
+    friend class DetourCrowdManager;
 
 public:
     /// Construct.
@@ -97,13 +111,15 @@ public:
     void SetDetailSampleMaxError(float error);
     /// Set padding of the navigation mesh bounding box. Having enough padding allows to add geometry on the extremities of the navigation mesh when doing partial rebuilds.
     void SetPadding(const Vector3& padding);
+    /// Set the cost of an area.
+    void SetAreaCost(unsigned areaID, float cost);
     /// Rebuild the navigation mesh. Return true if successful.
-    bool Build();
+    virtual bool Build();
     /// Rebuild part of the navigation mesh contained by the world-space bounding box. Return true if successful.
-    bool Build(const BoundingBox& boundingBox);
+    virtual bool Build(const BoundingBox& boundingBox);
     /// Find the nearest point on the navigation mesh to a given point. Extens specifies how far out from the specified point to check along each axis.
     Vector3 FindNearestPoint(const Vector3& point, const Vector3& extents=Vector3::ONE);
-    /// Try to move along the surface from one point to another
+    /// Try to move along the surface from one point to another.
     Vector3 MoveAlongSurface(const Vector3& start, const Vector3& end, const Vector3& extents=Vector3::ONE, int maxVisited=3);
     /// Find a path between world space points. Return non-empty list of points if successful. Extents specifies how far off the navigation mesh the points can be.
     void FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, const Vector3& extents = Vector3::ONE);
@@ -118,6 +134,10 @@ public:
     /// Add debug geometry to the debug renderer.
     void DrawDebugGeometry(bool depthTest);
 
+    /// Return the given name of this navigation mesh.
+    String GetMeshName() const { return meshName_; }
+    /// Set the name of this navigation mesh.
+    void SetMeshName(const String& newName);
     /// Return tile size.
     int GetTileSize() const { return tileSize_; }
     /// Return cell size.
@@ -146,6 +166,8 @@ public:
     float GetDetailSampleMaxError() const { return detailSampleMaxError_; }
     /// Return navigation mesh bounding box padding.
     const Vector3& GetPadding() const { return padding_; }
+    /// Get the current cost of an area
+    float GetAreaCost(unsigned areaID) const;
     /// Return whether has been initialized with valid navigation data.
     bool IsInitialized() const { return navMesh_ != 0; }
     /// Return local space bounding box of the navigation mesh.
@@ -155,27 +177,34 @@ public:
     /// Return number of tiles.
     IntVector2 GetNumTiles() const { return IntVector2(numTilesX_, numTilesZ_); }
 
+    /// Set the partition type used for polygon generation.
+    void SetPartitionType(NavmeshPartitionType aType);
+    /// Return Partition Type.
+    NavmeshPartitionType GetPartitionType() const { return partitionType_; }
+
     /// Set navigation data attribute.
-    void SetNavigationDataAttr(const PODVector<unsigned char>& value);
+    virtual void SetNavigationDataAttr(const PODVector<unsigned char>& value);
     /// Return navigation data attribute.
-    PODVector<unsigned char> GetNavigationDataAttr() const;
+    virtual PODVector<unsigned char> GetNavigationDataAttr() const;
 
-private:
+protected:
     /// Collect geometry from under Navigable components.
     void CollectGeometries(Vector<NavigationGeometryInfo>& geometryList);
     /// Visit nodes and collect navigable geometry.
     void CollectGeometries(Vector<NavigationGeometryInfo>& geometryList, Node* node, HashSet<Node*>& processedNodes, bool recursive);
     /// Get geometry data within a bounding box.
-    void GetTileGeometry(NavigationBuildData& build, Vector<NavigationGeometryInfo>& geometryList, BoundingBox& box);
+    void GetTileGeometry(NavBuildData* build, Vector<NavigationGeometryInfo>& geometryList, BoundingBox& box);
     /// Add a triangle mesh to the geometry data.
-    void AddTriMeshGeometry(NavigationBuildData& build, Geometry* geometry, const Matrix3x4& transform);
+    void AddTriMeshGeometry(NavBuildData* build, Geometry* geometry, const Matrix3x4& transform);
     /// Build one tile of the navigation mesh. Return true if successful.
-    bool BuildTile(Vector<NavigationGeometryInfo>& geometryList, int x, int z);
+    virtual bool BuildTile(Vector<NavigationGeometryInfo>& geometryList, int x, int z);
     /// Ensure that the navigation mesh query is initialized. Return true if successful.
     bool InitializeQuery();
     /// Release the navigation mesh and the query.
-    void ReleaseNavigationMesh();
+    virtual void ReleaseNavigationMesh();
 
+    /// Identifying name for this navigation mesh.
+    String meshName_;
     /// Detour navigation mesh.
     dtNavMesh* navMesh_;
     /// Detour navigation mesh query.
@@ -218,6 +247,13 @@ private:
     int numTilesZ_;
     /// Whole navigation mesh bounding box.
     BoundingBox boundingBox_;
+
+    /// Type of the heightfield partitioning.
+    NavmeshPartitionType partitionType_;
+    /// Keep internal build resources for debug draw modes.
+    bool keepInterResults_;
+    /// Internal build resources for creating the navmesh.
+    HashMap<Pair<int, int>, NavBuildData*> builds_;
 };
 
 /// Register Navigation library objects.

+ 16 - 0
Source/Atomic/Navigation/OffMeshConnection.cpp

@@ -34,6 +34,8 @@ namespace Atomic
 extern const char* NAVIGATION_CATEGORY;
 
 static const float DEFAULT_RADIUS = 1.0f;
+static const unsigned DEFAULT_MASK_FLAG = 1;
+static const unsigned DEFAULT_AREA = 0;
 
 OffMeshConnection::OffMeshConnection(Context* context) :
     Component(context),
@@ -56,6 +58,8 @@ void OffMeshConnection::RegisterObject(Context* context)
     ATTRIBUTE("Endpoint NodeID", int, endPointID_, 0, AM_DEFAULT | AM_NODEID);
     ATTRIBUTE("Radius", float, radius_, DEFAULT_RADIUS, AM_DEFAULT);
     ATTRIBUTE("Bidirectional", bool, bidirectional_, true, AM_DEFAULT);
+    ATTRIBUTE("Flags Mask", unsigned, mask_, DEFAULT_MASK_FLAG, AM_DEFAULT);
+    ATTRIBUTE("Area Type", unsigned, areaId_, DEFAULT_AREA, AM_DEFAULT);
 }
 
 void OffMeshConnection::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
@@ -101,6 +105,18 @@ void OffMeshConnection::SetBidirectional(bool enabled)
     MarkNetworkUpdate();
 }
 
+void OffMeshConnection::SetMask(unsigned newMask)
+{
+    mask_ = newMask;
+    MarkNetworkUpdate();
+}
+
+void OffMeshConnection::SetAreaID(unsigned newAreaID)
+{
+    areaId_ = newAreaID;
+    MarkNetworkUpdate();
+}
+
 void OffMeshConnection::SetEndPoint(Node* node)
 {
     endPoint_ = node;

+ 12 - 0
Source/Atomic/Navigation/OffMeshConnection.h

@@ -53,6 +53,10 @@ public:
     void SetRadius(float radius);
     /// Set bidirectional flag. Default true.
     void SetBidirectional(bool enabled);
+    /// Set a user assigned mask
+    void SetMask(unsigned newMask);
+    /// Sets the assigned area Id for the connection
+    void SetAreaID(unsigned newAreaID);
     
     /// Return endpoint node.
     Node* GetEndPoint() const;
@@ -60,6 +64,10 @@ public:
     float GetRadius() const { return radius_; }
     /// Return whether is bidirectional.
     bool IsBidirectional() const { return bidirectional_; }
+    /// Return the user assigned mask
+    unsigned GetMask() const { return mask_; }
+    /// Return the user assigned area ID
+    unsigned GetAreaID() const { return areaId_; }
     
 private:
     /// Endpoint node.
@@ -72,6 +80,10 @@ private:
     bool bidirectional_;
     /// Endpoint changed flag.
     bool endPointDirty_;
+    /// Flags mask to represent properties of this mesh
+    unsigned mask_;
+    /// Area id to be used for this off mesh connection's internal poly
+    unsigned areaId_;
 };
 
 }

+ 29 - 2
Source/Atomic/Network/Network.cpp

@@ -49,6 +49,8 @@ static const int DEFAULT_UPDATE_FPS = 30;
 Network::Network(Context* context) :
     Object(context),
     updateFps_(DEFAULT_UPDATE_FPS),
+    simulatedLatency_(0),
+    simulatedPacketLoss_(0.0f),
     updateInterval_(1.0f / (float)DEFAULT_UPDATE_FPS),
     updateAcc_(0.0f)
 {
@@ -122,7 +124,7 @@ void Network::HandleMessage(kNet::MessageConnection *source, kNet::packet_id_t p
     Connection* connection = GetConnection(source);
     if (connection)
     {
-        MemoryBuffer msg(data, numBytes);
+        MemoryBuffer msg(data, (unsigned)numBytes);
         if (connection->ProcessMessage(msgId, msg))
             return;
         
@@ -151,7 +153,7 @@ u32 Network::ComputeContentID(kNet::message_id_t msgId, const char* data, size_t
     case MSG_COMPONENTLATESTDATA:
         {
             // Return the node or component ID, which is first in the message
-            MemoryBuffer msg(data, numBytes);
+            MemoryBuffer msg(data, (unsigned)numBytes);
             return msg.ReadNetID();
         }
         
@@ -167,6 +169,7 @@ void Network::NewConnectionEstablished(kNet::MessageConnection* connection)
     
     // Create a new client connection corresponding to this MessageConnection
     SharedPtr<Connection> newConnection(new Connection(context_, true, kNet::SharedPtr<kNet::MessageConnection>(connection)));
+    newConnection->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
     clientConnections_[connection] = newConnection;
     LOGINFO("Client " + newConnection->ToString() + " connected");
     
@@ -216,6 +219,8 @@ bool Network::Connect(const String& address, unsigned short port, Scene* scene,
         serverConnection_->SetScene(scene);
         serverConnection_->SetIdentity(identity);
         serverConnection_->SetConnectPending(true);
+        serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
+
         LOGINFO("Connecting to server " + serverConnection_->ToString());
         return true;
     }
@@ -335,6 +340,18 @@ void Network::SetUpdateFps(int fps)
     updateAcc_ = 0.0f;
 }
 
+void Network::SetSimulatedLatency(int ms)
+{
+    simulatedLatency_ = Max(ms, 0);
+    ConfigureNetworkSimulator();
+}
+
+void Network::SetSimulatedPacketLoss(float loss)
+{
+    simulatedPacketLoss_ = Clamp(loss, 0.0f, 1.0f);
+    ConfigureNetworkSimulator();
+}
+
 void Network::RegisterRemoteEvent(StringHash eventType)
 {
     if (blacklistedRemoteEvents_.Find(eventType) != blacklistedRemoteEvents_.End())
@@ -566,6 +583,16 @@ void Network::OnServerDisconnected()
     }
 }
 
+void Network::ConfigureNetworkSimulator()
+{
+    if (serverConnection_)
+        serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
+
+    for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
+        i != clientConnections_.End(); ++i)
+        i->second_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
+}
+
 void RegisterNetworkLibrary(Context* context)
 {
     NetworkPriority::RegisterObject(context);

+ 14 - 0
Source/Atomic/Network/Network.h

@@ -83,6 +83,10 @@ public:
     void BroadcastRemoteEvent(Node* node, StringHash eventType, bool inOrder, const VariantMap& eventData = Variant::emptyVariantMap);
     /// Set network update FPS.
     void SetUpdateFps(int fps);
+    /// Set simulated latency in milliseconds. This adds a fixed delay before sending each packet.
+    void SetSimulatedLatency(int ms);
+    /// Set simulated packet loss probability between 0.0 - 1.0.
+    void SetSimulatedPacketLoss(float probability);
     /// Register a remote event as allowed to be received. There is also a fixed blacklist of events that can not be allowed in any case, such as ConsoleCommand.
     void RegisterRemoteEvent(StringHash eventType);
     /// Unregister a remote event as allowed to received.
@@ -98,6 +102,10 @@ public:
 
     /// Return network update FPS.
     int GetUpdateFps() const { return updateFps_; }
+    /// Return simulated latency in milliseconds.
+    int GetSimulatedLatency() const { return simulatedLatency_; }
+    /// Return simulated packet loss probability.
+    float GetSimulatedPacketLoss() const { return simulatedPacketLoss_; }
     /// Return a client or server connection by kNet MessageConnection, or null if none exist.
     Connection* GetConnection(kNet::MessageConnection* connection) const;
     /// Return the connection to the server. Null if not connected.
@@ -125,6 +133,8 @@ private:
     void OnServerConnected();
     /// Handle server disconnection.
     void OnServerDisconnected();
+    /// Reconfigure network simulator parameters on all existing connections.
+    void ConfigureNetworkSimulator();
     
     /// kNet instance.
     kNet::Network* network_;
@@ -140,6 +150,10 @@ private:
     HashSet<Scene*> networkScenes_;
     /// Update FPS.
     int updateFps_;
+    /// Simulated latency (send delay) in milliseconds.
+    int simulatedLatency_;
+    /// Simulated packet loss probability between 0.0 - 1.0.
+    float simulatedPacketLoss_;
     /// Update time interval.
     float updateInterval_;
     /// Update time accumulator.

+ 1 - 1
Source/Atomic/Physics/PhysicsWorld.cpp

@@ -700,7 +700,7 @@ void PhysicsWorld::PostStep(float timeStep)
     SendCollisionEvents();
 
     // Send post-step event
-    using namespace PhysicsPreStep;
+    using namespace PhysicsPostStep;
 
     VariantMap& eventData = GetEventDataMap();
     eventData[P_WORLD] = this;

+ 66 - 0
Source/Atomic/Resource/Image.cpp

@@ -1426,6 +1426,72 @@ SharedPtr<Image> Image::GetNextLevel() const
     return mipImage;
 }
 
+SharedPtr<Image> Image::ConvertToRGBA() const
+{
+    if (IsCompressed())
+    {
+        LOGERROR("Can not convert compressed image to RGBA");
+        return SharedPtr<Image>();
+    }
+    if (components_ < 1 || components_ > 4)
+    {
+        LOGERROR("Illegal number of image components for conversion to RGBA");
+        return SharedPtr<Image>();
+    }
+    if (!data_)
+    {
+        LOGERROR("Can not convert image without data to RGBA");
+        return SharedPtr<Image>();
+    }
+
+    // Already RGBA?
+    if (components_ == 4)
+        return SharedPtr<Image>(const_cast<Image*>(this));
+
+    SharedPtr<Image> ret(new Image(context_));
+    ret->SetSize(width_, height_, depth_, 4);
+    
+    const unsigned char* src = data_;
+    unsigned char* dest = ret->GetData();
+
+    switch (components_)
+    {
+    case 1:
+        for (unsigned i = 0; i < width_ * height_ * depth_; ++i)
+        {
+            unsigned char pixel = *src++;
+            *dest++ = pixel;
+            *dest++ = pixel;
+            *dest++ = pixel;
+            *dest++ = 255;
+        }
+        break;
+
+    case 2:
+        for (unsigned i = 0; i < width_ * height_ * depth_; ++i)
+        {
+            unsigned char pixel = *src++;
+            *dest++ = pixel;
+            *dest++ = pixel;
+            *dest++ = pixel;
+            *dest++ = *src++;
+        }
+        break;
+
+    case 3:
+        for (unsigned i = 0; i < width_ * height_ * depth_; ++i)
+        {
+            *dest++ = *src++;
+            *dest++ = *src++;
+            *dest++ = *src++;
+            *dest++ = 255;
+        }
+        break;
+    }
+
+    return ret;
+}
+
 CompressedLevel Image::GetCompressedLevel(unsigned index) const
 {
     CompressedLevel level;

+ 2 - 0
Source/Atomic/Resource/Image.h

@@ -169,6 +169,8 @@ public:
     unsigned GetNumCompressedLevels() const { return numCompressedLevels_; }
     /// Return next mip level by bilinear filtering.
     SharedPtr<Image> GetNextLevel() const;
+    /// Return image converted to 4-component (RGBA) to circumvent modern rendering API's not supporting e.g. the luminance-alpha format.
+    SharedPtr<Image> ConvertToRGBA() const;
     /// Return a compressed mip level.
     CompressedLevel GetCompressedLevel(unsigned index) const;
     /// Return subimage from the image by the defined rect or null if failed. 3D images are not supported. You must free the subimage yourself.

+ 2 - 3
Source/Atomic/Resource/JSONFile.cpp

@@ -94,9 +94,8 @@ bool JSONFile::Save(Serializer& dest, const String& indendation) const
     writer.SetIndent(!indendation.Empty() ?  indendation.Front() : '\0', indendation.Length());
 
     document_->Accept(writer);
-    dest.Write(buffer.GetString(), buffer.GetSize());
-
-    return true;
+    unsigned size = (unsigned)buffer.GetSize();
+    return dest.Write(buffer.GetString(), size) == size;
 }
 
 JSONValue JSONFile::CreateRoot(JSONValueType valueType)

+ 2 - 0
Source/Atomic/Resource/PListFile.h

@@ -43,6 +43,7 @@ enum PListValueType
 
 class PListValue;
 
+/// PList value map.
 class PListValueMap : public HashMap<String, PListValue>
 {
 public:
@@ -53,6 +54,7 @@ public:
 typedef Vector<PListValue> PListValueVector;
 
 class ATOMIC_API PListValue
+/// PList value.
 {
 public:
     // Construct.

+ 5 - 3
Source/Atomic/Resource/Resource.cpp

@@ -23,6 +23,7 @@
 #include "Precompiled.h"
 #include "../IO/Log.h"
 #include "../Core/Profiler.h"
+#include "../Core/Thread.h"
 #include "../Resource/Resource.h"
 
 namespace Atomic
@@ -47,12 +48,13 @@ bool Resource::Load(Deserializer& source)
         profiler->BeginBlock(profileBlockName.CString());
 #endif
 
-    // Make sure any previous async state is cancelled
-    SetAsyncLoadState(ASYNC_DONE);
-
+    // If we are loading synchronously in a non-main thread, behave as if async loading (for example use
+    // GetTempResource() instead of GetResource() to load resource dependencies)
+    SetAsyncLoadState(Thread::IsMainThread() ? ASYNC_DONE : ASYNC_LOADING);
     bool success = BeginLoad(source);
     if (success)
         success &= EndLoad();
+    SetAsyncLoadState(ASYNC_DONE);
 
 #ifdef ATOMIC_PROFILING
     if (profiler)

+ 21 - 2
Source/Atomic/Resource/ResourceCache.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -144,7 +144,7 @@ bool ResourceCache::AddPackageFile(PackageFile* package, unsigned priority)
 bool ResourceCache::AddPackageFile(const String& fileName, unsigned priority)
 {
     SharedPtr<PackageFile> package(new PackageFile(context_));
-    return package->Open(fileName) && AddPackageFile(package, priority);
+    return package->Open(fileName) && AddPackageFile(package);
 }
 
 bool ResourceCache::AddManualResource(Resource* resource)
@@ -500,6 +500,25 @@ SharedPtr<File> ResourceCache::GetFile(const String& nameIn, bool sendEventOnFai
     return SharedPtr<File>();
 }
 
+Resource* ResourceCache::GetExistingResource(StringHash type, const String& nameIn)
+{
+    String name = SanitateResourceName(nameIn);
+
+    if (!Thread::IsMainThread())
+    {
+        LOGERROR("Attempted to get resource " + name + " from outside the main thread");
+        return 0;
+    }
+
+    // If empty name, return null pointer immediately
+    if (name.Empty())
+        return 0;
+
+    StringHash nameHash(name);
+
+    const SharedPtr<Resource>& existing = FindResource(type, nameHash);
+    return existing;
+}
 Resource* ResourceCache::GetResource(StringHash type, const String& nameIn, bool sendEventOnFailure)
 {
     String name = SanitateResourceName(nameIn);

+ 11 - 1
Source/Atomic/Resource/ResourceCache.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -141,6 +141,8 @@ public:
     unsigned GetNumBackgroundLoadResources() const;
     /// Return all loaded resources of a specific type.
     void GetResources(PODVector<Resource*>& result, StringHash type) const;
+    /// Return an already loaded resource of specific type & name, or null if not found. Will not load if does not exist.
+    Resource* GetExistingResource(StringHash type, const String& name);
     /// Return all loaded resources.
     const HashMap<StringHash, ResourceGroup>& GetAllResources() const { return resourceGroups_; }
     /// Return added resource load directories.
@@ -149,6 +151,8 @@ public:
     const Vector<SharedPtr<PackageFile> >& GetPackageFiles() const { return packages_; }
     /// Template version of returning a resource by name.
     template <class T> T* GetResource(const String& name, bool sendEventOnFailure = true);
+    /// Template version of returning an existing resource by name.
+    template <class T> T* GetExistingResource(const String& name);
     /// Template version of loading a resource without storing it to the cache.
     template <class T> SharedPtr<T> GetTempResource(const String& name, bool sendEventOnFailure = true);
     /// Template version of queueing a resource background load.
@@ -229,6 +233,12 @@ private:
     int finishBackgroundResourcesMs_;
 };
 
+template <class T> T* ResourceCache::GetExistingResource(const String& name)
+{
+    StringHash type = T::GetTypeStatic();
+    return static_cast<T*>(GetExistingResource(type, name));
+}
+
 template <class T> T* ResourceCache::GetResource(const String& name, bool sendEventOnFailure)
 {
     StringHash type = T::GetTypeStatic();

+ 3 - 3
Source/Atomic/Resource/XMLElement.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -921,7 +921,7 @@ XMLElement XPathResultSet::FirstResult()
 
 unsigned XPathResultSet::Size() const
 {
-    return resultSet_ ? resultSet_->size() : 0;
+    return resultSet_ ? (unsigned)resultSet_->size() : 0;
 }
 
 bool XPathResultSet::Empty() const
@@ -1070,7 +1070,7 @@ String XPathQuery::EvaluateToString(XMLElement element) const
 
     const pugi::xml_node& node = element.GetXPathNode() ? element.GetXPathNode()->node(): pugi::xml_node(element.GetNode());
     String result;
-    result.Reserve(query_->evaluate_string(0, 0, node));    // First call get the size
+    result.Reserve((unsigned)query_->evaluate_string(0, 0, node));    // First call get the size
     query_->evaluate_string(const_cast<pugi::char_t*>(result.CString()), result.Capacity(), node);  // Second call get the actual string
     return result;
 }

+ 4 - 2
Source/Atomic/Resource/XMLFile.cpp

@@ -53,7 +53,7 @@ public:
     /// Write bytes to output.
     void write(const void* data, size_t size)
     {
-        if (dest_.Write(data, size) != size)
+        if (dest_.Write(data, (unsigned)size) != size)
             success_ = false;
     }
 
@@ -106,7 +106,9 @@ bool XMLFile::BeginLoad(Deserializer& source)
     {
         // The existence of this attribute indicates this is an RFC 5261 patch file
         ResourceCache* cache = GetSubsystem<ResourceCache>();
-        XMLFile* inheritedXMLFile = cache->GetResource<XMLFile>(inherit);
+        // If being async loaded, GetResource() is not safe, so use GetTempResource() instead
+        XMLFile* inheritedXMLFile = GetAsyncLoadState() == ASYNC_DONE ? cache->GetResource<XMLFile>(inherit) :
+            cache->GetTempResource<XMLFile>(inherit);
         if (!inheritedXMLFile)
         {
             LOGERRORF("Could not find inherited XML file: %s", inherit.CString());