Browse Source

Normalize resource names in SanitateResourceName() if they also contain a registered resource dir name, ie. Data/Scripts/NinjaSnowWar.as becomes Scripts/NinjaSnowWar.as. This fixes script file possibly getting loaded twice.
Fixed renderer raycasts returning wrong distance.
Added caseSensitive parameter to several String functions.
Added option to convert file extension to lowercase in GetExtension() and GetFileNameAndExtension() functions.
Removed unnecessary lowercase conversion of extensions, and complicated string compare logic.

Lasse Öörni 12 years ago
parent
commit
372fbed88d

+ 106 - 34
Source/Engine/Container/Str.cpp

@@ -202,22 +202,34 @@ String& String::operator += (bool rhs)
     return *this += String(rhs);
     return *this += String(rhs);
 }
 }
 
 
-void String::Replace(char replaceThis, char replaceWith)
+void String::Replace(char replaceThis, char replaceWith, bool caseSensitive)
 {
 {
-    for (unsigned i = 0; i < length_; ++i)
+    if (caseSensitive)
     {
     {
-        if (buffer_[i] == replaceThis)
-            buffer_[i] = replaceWith;
+        for (unsigned i = 0; i < length_; ++i)
+        {
+            if (buffer_[i] == replaceThis)
+                buffer_[i] = replaceWith;
+        }
+    }
+    else
+    {
+        replaceThis = tolower(replaceThis);
+        for (unsigned i = 0; i < length_; ++i)
+        {
+            if (tolower(buffer_[i]) == replaceThis)
+                buffer_[i] = replaceWith;
+        }
     }
     }
 }
 }
 
 
-void String::Replace(const String& replaceThis, const String& replaceWith)
+void String::Replace(const String& replaceThis, const String& replaceWith, bool caseSensitive)
 {
 {
     unsigned nextPos = 0;
     unsigned nextPos = 0;
     
     
     while (nextPos < length_)
     while (nextPos < length_)
     {
     {
-        unsigned pos = Find(replaceThis, nextPos);
+        unsigned pos = Find(replaceThis, nextPos, caseSensitive);
         if (pos == NPOS)
         if (pos == NPOS)
             break;
             break;
         Replace(pos, replaceThis.length_, replaceWith);
         Replace(pos, replaceThis.length_, replaceWith);
@@ -225,13 +237,22 @@ void String::Replace(const String& replaceThis, const String& replaceWith)
     }
     }
 }
 }
 
 
-void String::Replace(unsigned pos, unsigned length, const String& str)
+void String::Replace(unsigned pos, unsigned length, const String& replaceWith)
 {
 {
     // If substring is illegal, do nothing
     // If substring is illegal, do nothing
     if (pos + length > length_)
     if (pos + length > length_)
         return;
         return;
     
     
-    Replace(pos, length, str.buffer_, str.length_);
+    Replace(pos, length, replaceWith.buffer_, replaceWith.length_);
+}
+
+void String::Replace(unsigned pos, unsigned length, const char* replaceWith)
+{
+    // If substring is illegal, do nothing
+    if (pos + length > length_)
+        return;
+    
+    Replace(pos, length, replaceWith, CStringLength(replaceWith));
 }
 }
 
 
 String::Iterator String::Replace(const String::Iterator& start, const String::Iterator& end, const String& replaceWith)
 String::Iterator String::Replace(const String::Iterator& start, const String::Iterator& end, const String& replaceWith)
@@ -245,17 +266,17 @@ String::Iterator String::Replace(const String::Iterator& start, const String::It
     return Begin() + pos;
     return Begin() + pos;
 }
 }
 
 
-String String::Replaced(char replaceThis, char replaceWith) const
+String String::Replaced(char replaceThis, char replaceWith, bool caseSensitive) const
 {
 {
     String ret(*this);
     String ret(*this);
-    ret.Replace(replaceThis, replaceWith);
+    ret.Replace(replaceThis, replaceWith, caseSensitive);
     return ret;
     return ret;
 }
 }
 
 
-String String::Replaced(const String& replaceThis, const String& replaceWith) const
+String String::Replaced(const String& replaceThis, const String& replaceWith,  bool caseSensitive) const
 {
 {
     String ret(*this);
     String ret(*this);
-    ret.Replace(replaceThis, replaceWith);
+    ret.Replace(replaceThis, replaceWith, caseSensitive);
     return ret;
     return ret;
 }
 }
 
 
@@ -518,36 +539,62 @@ void String::Join(const Vector<String>& subStrings, String glue)
     *this = Joined(subStrings, glue);
     *this = Joined(subStrings, glue);
 }
 }
 
 
-unsigned String::Find(char c, unsigned startPos) const
+unsigned String::Find(char c, unsigned startPos, bool caseSensitive) const
 {
 {
-    for (unsigned i = startPos; i < length_; ++i)
+    if (caseSensitive)
+    {
+        for (unsigned i = startPos; i < length_; ++i)
+        {
+            if (buffer_[i] == c)
+                return i;
+        }
+    }
+    else
     {
     {
-        if (buffer_[i] == c)
-            return i;
+        c = tolower(c);
+        for (unsigned i = startPos; i < length_; ++i)
+        {
+            if (tolower(buffer_[i]) == c)
+                return i;
+        }
     }
     }
     
     
     return NPOS;
     return NPOS;
 }
 }
 
 
-unsigned String::Find(const String& str, unsigned startPos) const
+unsigned String::Find(const String& str, unsigned startPos, bool caseSensitive) const
 {
 {
     if (!str.length_ || str.length_ > length_)
     if (!str.length_ || str.length_ > length_)
         return NPOS;
         return NPOS;
     
     
     char first = str.buffer_[0];
     char first = str.buffer_[0];
-    
+    if (!caseSensitive)
+        first = tolower(first);
+
     for (unsigned i = startPos; i <= length_ - str.length_; ++i)
     for (unsigned i = startPos; i <= length_ - str.length_; ++i)
     {
     {
-        if (buffer_[i] == first)
+        char c = buffer_[i];
+        if (!caseSensitive)
+            c = tolower(c);
+
+        if (c == first)
         {
         {
             unsigned skip = NPOS;
             unsigned skip = NPOS;
             bool found = true;
             bool found = true;
             for (unsigned j = 1; j < str.length_; ++j)
             for (unsigned j = 1; j < str.length_; ++j)
             {
             {
-                char c = buffer_[i + j];
+                c = buffer_[i + j];
+                char d = str.buffer_[j];
+                if (!caseSensitive)
+                {
+                    c = tolower(c);
+                    d = tolower(d);
+                }
+
                 if (skip == NPOS && c == first)
                 if (skip == NPOS && c == first)
                     skip = i + j - 1;
                     skip = i + j - 1;
-                if (c != str.buffer_[j])
+
+                if (c != d)
                 {
                 {
                     found = false;
                     found = false;
                     if (skip != NPOS)
                     if (skip != NPOS)
@@ -563,21 +610,33 @@ unsigned String::Find(const String& str, unsigned startPos) const
     return NPOS;
     return NPOS;
 }
 }
 
 
-unsigned String::FindLast(char c, unsigned startPos) const
+unsigned String::FindLast(char c, unsigned startPos, bool caseSensitive) const
 {
 {
     if (startPos >= length_)
     if (startPos >= length_)
         startPos = length_ - 1;
         startPos = length_ - 1;
     
     
-    for (unsigned i = startPos; i < length_; --i)
+    if (caseSensitive)
+    {
+        for (unsigned i = startPos; i < length_; --i)
+        {
+            if (buffer_[i] == c)
+                return i;
+        }
+    }
+    else
     {
     {
-        if (buffer_[i] == c)
-            return i;
+        c = tolower(c);
+        for (unsigned i = startPos; i < length_; --i)
+        {
+            if (tolower(buffer_[i]) == c)
+                return i;
+        }
     }
     }
     
     
     return NPOS;
     return NPOS;
 }
 }
 
 
-unsigned String::FindLast(const String& str, unsigned startPos) const
+unsigned String::FindLast(const String& str, unsigned startPos, bool caseSensitive) const
 {
 {
     if (!str.length_ || str.length_ > length_)
     if (!str.length_ || str.length_ > length_)
         return NPOS;
         return NPOS;
@@ -585,16 +644,29 @@ unsigned String::FindLast(const String& str, unsigned startPos) const
         startPos = length_ - str.length_;
         startPos = length_ - str.length_;
     
     
     char first = str.buffer_[0];
     char first = str.buffer_[0];
-    
+    if (!caseSensitive)
+        first = tolower(first);
+
     for (unsigned i = startPos; i < length_; --i)
     for (unsigned i = startPos; i < length_; --i)
     {
     {
-        if (buffer_[i] == first)
+        char c = buffer_[i];
+        if (!caseSensitive)
+            c = tolower(c);
+
+        if (c == first)
         {
         {
             bool found = true;
             bool found = true;
             for (unsigned j = 1; j < str.length_; ++j)
             for (unsigned j = 1; j < str.length_; ++j)
             {
             {
-                char c = buffer_[i + j];
-                if (c != str.buffer_[j])
+                c = buffer_[i + j];
+                char d = str.buffer_[j];
+                if (!caseSensitive)
+                {
+                    c = tolower(c);
+                    d = tolower(d);
+                }
+
+                if (c != d)
                 {
                 {
                     found = false;
                     found = false;
                     break;
                     break;
@@ -608,14 +680,14 @@ unsigned String::FindLast(const String& str, unsigned startPos) const
     return NPOS;
     return NPOS;
 }
 }
 
 
-bool String::StartsWith(const String& str) const
+bool String::StartsWith(const String& str, bool caseSensitive) const
 {
 {
-    return Find(str) == 0;
+    return Find(str, 0, caseSensitive) == 0;
 }
 }
 
 
-bool String::EndsWith(const String& str) const
+bool String::EndsWith(const String& str, bool caseSensitive) const
 {
 {
-    return FindLast(str) == Length() - str.Length();
+    return FindLast(str, Length() - 1, caseSensitive) == Length() - str.Length();
 }
 }
 
 
 int String::Compare(const String& str, bool caseSensitive) const
 int String::Compare(const String& str, bool caseSensitive) const

+ 12 - 10
Source/Engine/Container/Str.h

@@ -274,17 +274,19 @@ public:
     const char& At(unsigned index) const { assert(index < length_); return buffer_[index]; }
     const char& At(unsigned index) const { assert(index < length_); return buffer_[index]; }
     
     
     /// Replace all occurrences of a character.
     /// Replace all occurrences of a character.
-    void Replace(char replaceThis, char replaceWith);
+    void Replace(char replaceThis, char replaceWith, bool caseSensitive = true);
     /// Replace all occurrences of a string.
     /// Replace all occurrences of a string.
-    void Replace(const String& replaceThis, const String& replaceWith);
+    void Replace(const String& replaceThis, const String& replaceWith, bool caseSensitive = true);
     /// Replace a substring.
     /// Replace a substring.
     void Replace(unsigned pos, unsigned length, const String& replaceWith);
     void Replace(unsigned pos, unsigned length, const String& replaceWith);
+    /// Replace a substring with a C string.
+    void Replace(unsigned pos, unsigned length, const char* replaceWith);
     /// Replace a substring by iterators.
     /// Replace a substring by iterators.
     Iterator Replace(const Iterator& start, const Iterator& end, const String& replaceWith);
     Iterator Replace(const Iterator& start, const Iterator& end, const String& replaceWith);
     /// Return a string with all occurrences of a character replaced.
     /// Return a string with all occurrences of a character replaced.
-    String Replaced(char replaceThis, char replaceWith) const;
+    String Replaced(char replaceThis, char replaceWith, bool caseSensitive = true) const;
     /// Return a string with all occurrences of a string replaced.
     /// Return a string with all occurrences of a string replaced.
-    String Replaced(const String& replaceThis, const String& replaceWith) const;
+    String Replaced(const String& replaceThis, const String& replaceWith, bool caseSensitive = true) const;
     /// Append a string.
     /// Append a string.
     String& Append(const String& str);
     String& Append(const String& str);
     /// Append a C string.
     /// Append a C string.
@@ -347,17 +349,17 @@ public:
     /// Join substrings with a 'glue' string.
     /// Join substrings with a 'glue' string.
     void Join(const Vector<String>& subStrings, String glue);
     void Join(const Vector<String>& subStrings, String glue);
     /// Return index to the first occurrence of a string, or NPOS if not found.
     /// Return index to the first occurrence of a string, or NPOS if not found.
-    unsigned Find(const String& str, unsigned startPos = 0) const;
+    unsigned Find(const String& str, unsigned startPos = 0, bool caseSensitive = true) const;
     /// Return index to the first occurrence of a character, or NPOS if not found.
     /// Return index to the first occurrence of a character, or NPOS if not found.
-    unsigned Find(char c, unsigned startPos = 0) const;
+    unsigned Find(char c, unsigned startPos = 0, bool caseSensitive = true) const;
     /// Return index to the last occurrence of a string, or NPOS if not found.
     /// Return index to the last occurrence of a string, or NPOS if not found.
-    unsigned FindLast(const String& str, unsigned startPos = NPOS) const;
+    unsigned FindLast(const String& str, unsigned startPos = NPOS, bool caseSensitive = true) const;
     /// Return index to the last occurrence of a character, or NPOS if not found.
     /// Return index to the last occurrence of a character, or NPOS if not found.
-    unsigned FindLast(char c, unsigned startPos = NPOS) const;
+    unsigned FindLast(char c, unsigned startPos = NPOS, bool caseSensitive = true) const;
     /// Return whether starts with a string.
     /// Return whether starts with a string.
-    bool StartsWith(const String& str) const;
+    bool StartsWith(const String& str, bool caseSensitive = true) const;
     /// Return whether ends with a string.
     /// Return whether ends with a string.
-    bool EndsWith(const String& str) const;
+    bool EndsWith(const String& str, bool casSensitive = true) const;
     /// Return the C string.
     /// Return the C string.
     const char* CString() const { return buffer_; }
     const char* CString() const { return buffer_; }
     /// Return length.
     /// Return length.

+ 3 - 3
Source/Engine/Engine/Console.cpp

@@ -108,10 +108,10 @@ void Console::SetDefaultStyle(XMLFile* style)
 void Console::SetVisible(bool enable)
 void Console::SetVisible(bool enable)
 {
 {
     // Check if we have script subsystem
     // Check if we have script subsystem
-    bool hasScriptSubsystem = GetSubsystem<Script>();
+    bool hasScriptSubsystem = GetSubsystem<Script>() != 0;
     #ifdef ENABLE_LUA
     #ifdef ENABLE_LUA
     if (!hasScriptSubsystem)
     if (!hasScriptSubsystem)
-        hasScriptSubsystem = GetSubsystem<LuaScript>();
+        hasScriptSubsystem = GetSubsystem<LuaScript>() != 0;
     #endif
     #endif
 
 
     lineEdit_->SetVisible(hasScriptSubsystem);
     lineEdit_->SetVisible(hasScriptSubsystem);
@@ -186,7 +186,7 @@ bool Console::IsVisible() const
 
 
 unsigned Console::GetNumRows() const
 unsigned Console::GetNumRows() const
 {
 {
-    rowContainer_->GetNumChildren();
+    return rowContainer_->GetNumChildren();
 }
 }
 
 
 const String& Console::GetHistoryRow(unsigned index) const
 const String& Console::GetHistoryRow(unsigned index) const

+ 1 - 1
Source/Engine/Graphics/AnimatedModel.cpp

@@ -162,7 +162,7 @@ void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQu
             {
             {
                 // Follow with an OBB test if required
                 // Follow with an OBB test if required
                 Matrix3x4 inverse = transform.Inverse();
                 Matrix3x4 inverse = transform.Inverse();
-                Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+                Ray localRay = query.ray_.Transformed(inverse);
                 distance = localRay.HitDistance(box);
                 distance = localRay.HitDistance(box);
                 if (distance >= query.maxDistance_)
                 if (distance >= query.maxDistance_)
                     continue;
                     continue;

+ 1 - 1
Source/Engine/Graphics/CustomGeometry.cpp

@@ -89,7 +89,7 @@ void CustomGeometry::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQ
     case RAY_OBB:
     case RAY_OBB:
     case RAY_TRIANGLE:
     case RAY_TRIANGLE:
         Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
         Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-        Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+        Ray localRay = query.ray_.Transformed(inverse);
         float distance = localRay.HitDistance(boundingBox_);
         float distance = localRay.HitDistance(boundingBox_);
         if (distance < query.maxDistance_)
         if (distance < query.maxDistance_)
         {
         {

+ 1 - 1
Source/Engine/Graphics/Light.cpp

@@ -175,7 +175,7 @@ void Light::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResul
     case RAY_OBB:
     case RAY_OBB:
         {
         {
             Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
             Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-            Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+            Ray localRay = query.ray_.Transformed(inverse);
             distance = localRay.HitDistance(GetWorldBoundingBox().Transformed(inverse));
             distance = localRay.HitDistance(GetWorldBoundingBox().Transformed(inverse));
             if (distance >= query.maxDistance_)
             if (distance >= query.maxDistance_)
                 return;
                 return;

+ 1 - 1
Source/Engine/Graphics/StaticModel.cpp

@@ -84,7 +84,7 @@ void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuer
     case RAY_OBB:
     case RAY_OBB:
     case RAY_TRIANGLE:
     case RAY_TRIANGLE:
         Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
         Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-        Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+        Ray localRay = query.ray_.Transformed(inverse);
         float distance = localRay.HitDistance(boundingBox_);
         float distance = localRay.HitDistance(boundingBox_);
         if (distance < query.maxDistance_)
         if (distance < query.maxDistance_)
         {
         {

+ 1 - 1
Source/Engine/Graphics/TerrainPatch.cpp

@@ -85,7 +85,7 @@ void TerrainPatch::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQue
     case RAY_OBB:
     case RAY_OBB:
     case RAY_TRIANGLE:
     case RAY_TRIANGLE:
         Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
         Matrix3x4 inverse(node_->GetWorldTransform().Inverse());
-        Ray localRay(inverse * query.ray_.origin_, inverse * Vector4(query.ray_.direction_, 0.0f));
+        Ray localRay = query.ray_.Transformed(inverse);
         float distance = localRay.HitDistance(boundingBox_);
         float distance = localRay.HitDistance(boundingBox_);
         if (distance < query.maxDistance_)
         if (distance < query.maxDistance_)
         {
         {

+ 8 - 6
Source/Engine/IO/FileSystem.cpp

@@ -586,7 +586,7 @@ void FileSystem::ScanDirInternal(Vector<String>& result, String path, const Stri
     #endif
     #endif
 }
 }
 
 
-void SplitPath(const String& fullPath, String& pathName, String& fileName, String& extension)
+void SplitPath(const String& fullPath, String& pathName, String& fileName, String& extension, bool lowercaseExtension)
 {
 {
     String fullPathCopy = GetInternalPath(fullPath);
     String fullPathCopy = GetInternalPath(fullPath);
 
 
@@ -595,7 +595,9 @@ void SplitPath(const String& fullPath, String& pathName, String& fileName, Strin
 
 
     if (extPos != String::NPOS && (pathPos == String::NPOS || extPos > pathPos))
     if (extPos != String::NPOS && (pathPos == String::NPOS || extPos > pathPos))
     {
     {
-        extension = fullPathCopy.Substring(extPos).ToLower();
+        extension = fullPathCopy.Substring(extPos);
+        if (lowercaseExtension)
+            extension = extension.ToLower();
         fullPathCopy = fullPathCopy.Substring(0, extPos);
         fullPathCopy = fullPathCopy.Substring(0, extPos);
     }
     }
     else
     else
@@ -628,17 +630,17 @@ String GetFileName(const String& fullPath)
     return file;
     return file;
 }
 }
 
 
-String GetExtension(const String& fullPath)
+String GetExtension(const String& fullPath, bool lowercaseExtension)
 {
 {
     String path, file, extension;
     String path, file, extension;
-    SplitPath(fullPath, path, file, extension);
+    SplitPath(fullPath, path, file, extension, lowercaseExtension);
     return extension;
     return extension;
 }
 }
 
 
-String GetFileNameAndExtension(const String& fileName)
+String GetFileNameAndExtension(const String& fileName, bool lowercaseExtension)
 {
 {
     String path, file, extension;
     String path, file, extension;
-    SplitPath(fileName, path, file, extension);
+    SplitPath(fileName, path, file, extension, lowercaseExtension);
     return file + extension;
     return file + extension;
 }
 }
 
 

+ 6 - 6
Source/Engine/IO/FileSystem.h

@@ -94,16 +94,16 @@ private:
     mutable String programDir_;
     mutable String programDir_;
 };
 };
 
 
-/// Split a full path to path, filename and extension. The extension will be converted to lowercase.
-URHO3D_API void SplitPath(const String& fullPath, String& pathName, String& fileName, String& extension);
+/// Split a full path to path, filename and extension. The extension will be converted to lowercase by default.
+URHO3D_API void SplitPath(const String& fullPath, String& pathName, String& fileName, String& extension, bool lowercaseExtension = true);
 /// Return the path from a full path.
 /// Return the path from a full path.
 URHO3D_API String GetPath(const String& fullPath);
 URHO3D_API String GetPath(const String& fullPath);
 /// Return the filename from a full path.
 /// Return the filename from a full path.
 URHO3D_API String GetFileName(const String& fullPath);
 URHO3D_API String GetFileName(const String& fullPath);
-/// Return the extension from a full path, converted to lowercase.
-URHO3D_API String GetExtension(const String& fullPath);
-/// Return the filename and extension from a full path. The extension will be converted to lowercase.
-URHO3D_API String GetFileNameAndExtension(const String& fullPath);
+/// Return the extension from a full path, converted to lowercase by default.
+URHO3D_API String GetExtension(const String& fullPath, bool lowercaseExtension = true);
+/// Return the filename and extension from a full path. The case of the extension is preserved by default, so that the file can be opened in case-sensitive operating systems.
+URHO3D_API String GetFileNameAndExtension(const String& fullPath, bool lowercaseExtension = false);
 /// Replace the extension of a file name with another.
 /// Replace the extension of a file name with another.
 URHO3D_API String ReplaceExtension(const String& fullPath, const String& newExtension);
 URHO3D_API String ReplaceExtension(const String& fullPath, const String& newExtension);
 /// Add a slash at the end of the path if missing and convert to internal format (use slashes.)
 /// Add a slash at the end of the path if missing and convert to internal format (use slashes.)

+ 7 - 0
Source/Engine/Math/Ray.cpp

@@ -395,5 +395,12 @@ bool Ray::InsideGeometry(const void* vertexData, unsigned vertexSize, const void
     return false;
     return false;
 }
 }
 
 
+Ray Ray::Transformed(const Matrix3x4& transform) const
+{
+    Ray ret;
+    ret.origin_ = transform * origin_;
+    ret.direction_ = transform * Vector4(direction_, 0.0f);
+    return ret;
+}
 
 
 }
 }

+ 5 - 2
Source/Engine/Math/Ray.h

@@ -23,6 +23,7 @@
 #pragma once
 #pragma once
 
 
 #include "Vector3.h"
 #include "Vector3.h"
+#include "Matrix3x4.h"
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {
@@ -41,7 +42,7 @@ public:
     {
     {
     }
     }
     
     
-    /// Construct from origin and direction.
+    /// Construct from origin and direction. The direction will be normalized.
     Ray(const Vector3& origin, const Vector3& direction) :
     Ray(const Vector3& origin, const Vector3& direction) :
         origin_(origin),
         origin_(origin),
         direction_(direction.Normalized())
         direction_(direction.Normalized())
@@ -99,7 +100,9 @@ public:
     bool InsideGeometry(const void* vertexData, unsigned vertexSize, unsigned vertexStart, unsigned vertexCount) const;
     bool InsideGeometry(const void* vertexData, unsigned vertexSize, unsigned vertexStart, unsigned vertexCount) const;
     /// Return whether ray is inside indexed geometry.
     /// Return whether ray is inside indexed geometry.
     bool InsideGeometry(const void* vertexData, unsigned vertexSize, const void* indexData, unsigned indexSize, unsigned indexStart, unsigned indexCount) const;
     bool InsideGeometry(const void* vertexData, unsigned vertexSize, const void* indexData, unsigned indexSize, unsigned indexStart, unsigned indexCount) const;
-    
+    /// Return transformed by a 3x4 matrix. This may result in a non-normalized direction.
+    Ray Transformed(const Matrix3x4& transform) const;
+
     /// Ray origin.
     /// Ray origin.
     Vector3 origin_;
     Vector3 origin_;
     /// Ray direction.
     /// Ray direction.

+ 22 - 0
Source/Engine/Resource/ResourceCache.cpp

@@ -581,6 +581,28 @@ String ResourceCache::SanitateResourceName(const String& nameIn) const
     String name = GetInternalPath(nameIn);
     String name = GetInternalPath(nameIn);
     name.Replace("../", "");
     name.Replace("../", "");
     name.Replace("./", "");
     name.Replace("./", "");
+
+    // If the path refers to one of the resource directories, normalize the resource name
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+    if (fileSystem && resourceDirs_.Size())
+    {
+        String namePath = GetPath(name);
+        String exePath = fileSystem->GetProgramDir();
+        for (unsigned i = 0; i < resourceDirs_.Size(); ++i)
+        {
+            String relativeResourcePath = resourceDirs_[i];
+            if (relativeResourcePath.StartsWith(exePath))
+                relativeResourcePath = relativeResourcePath.Substring(exePath.Length());
+            
+            if (namePath.StartsWith(resourceDirs_[i], false))
+                namePath = namePath.Substring(resourceDirs_[i].Length());
+            else if (namePath.StartsWith(relativeResourcePath, false))
+                namePath = namePath.Substring(relativeResourcePath.Length());
+        }
+
+        name = namePath + GetFileNameAndExtension(name);
+    }
+
     return name;
     return name;
 }
 }
 
 

+ 1 - 1
Source/Engine/Resource/ResourceCache.h

@@ -133,7 +133,7 @@ public:
     
     
     /// Return either the path itself or its parent, based on which of them has recognized resource subdirectories.
     /// Return either the path itself or its parent, based on which of them has recognized resource subdirectories.
     String GetPreferredResourceDir(const String& path) const;
     String GetPreferredResourceDir(const String& path) const;
-    /// Remove unsupported constructs from the resource name to prevent ambiguity.
+    /// Remove unsupported constructs from the resource name to prevent ambiguity, and normalize absolute filename to resource path relative if possible.
     String SanitateResourceName(const String& name) const;
     String SanitateResourceName(const String& name) const;
     /// Store a hash-to-name mapping.
     /// Store a hash-to-name mapping.
     void StoreNameHash(const String& name);
     void StoreNameHash(const String& name);

+ 10 - 10
Source/Engine/Script/Addons.cpp

@@ -1594,17 +1594,17 @@ void RegisterString(asIScriptEngine *engine)
     engine->RegisterObjectMethod("String", "String opAdd(const String&in) const", asMETHODPR(String, operator +, (const String&) const, String), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "String opAdd(const String&in) const", asMETHODPR(String, operator +, (const String&) const, String), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("String", "uint8 &opIndex(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("String", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("String", "const uint8 &opIndex(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("String", "void Replace(uint8, uint8)", asMETHODPR(String, Replace, (char, char), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("String", "void Replace(const String&in, const String&in)", asMETHODPR(String, Replace, (const String&, const String&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod("String", "String Replaced(uint8, uint8) const", asMETHODPR(String, Replaced, (char, char) const, String), asCALL_THISCALL);
-    engine->RegisterObjectMethod("String", "String Replaced(const String&in, const String&in) const", asMETHODPR(String, Replaced, (const String&, const String&) const, String), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "void Replace(uint8, uint8, bool caseSensitive = true)", asMETHODPR(String, Replace, (char, char, bool), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "void Replace(const String&in, const String&in, bool caseSensitive = true)", asMETHODPR(String, Replace, (const String&, const String&, bool), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "String Replaced(uint8, uint8, bool caseSensitive = true) const", asMETHODPR(String, Replaced, (char, char, bool) const, String), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "String Replaced(const String&in, const String&in, bool caseSensitive = true) const", asMETHODPR(String, Replaced, (const String&, const String&, bool) const, String), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "void Resize(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("String", "void Resize(uint)", asFUNCTION(StringResize), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod("String", "uint Find(const String&in, uint start = 0) const", asMETHODPR(String, Find, (const String&, unsigned) const, unsigned), asCALL_THISCALL);
-    engine->RegisterObjectMethod("String", "uint Find(uint8, uint start = 0) const", asMETHODPR(String, Find, (char, unsigned) const, unsigned), asCALL_THISCALL);
-    engine->RegisterObjectMethod("String", "uint FindLast(const String&in, uint start = 0xffffffff) const", asMETHODPR(String, FindLast, (const String&, unsigned) const, unsigned), asCALL_THISCALL);
-    engine->RegisterObjectMethod("String", "uint FindLast(uint8, uint start = 0xffffffff) const", asMETHODPR(String, FindLast, (char, unsigned) const, unsigned), asCALL_THISCALL);
-    engine->RegisterObjectMethod("String", "bool StartsWith(const String&in) const", asMETHOD(String, StartsWith), asCALL_THISCALL);
-    engine->RegisterObjectMethod("String", "bool EndsWith(const String&in) const", asMETHOD(String, EndsWith), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "uint Find(const String&in, uint start = 0, bool caseSensitive = true) const", asMETHODPR(String, Find, (const String&, unsigned, bool) const, unsigned), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "uint Find(uint8, uint start = 0, bool caseSensitive = true) const", asMETHODPR(String, Find, (char, unsigned, bool) const, unsigned), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "uint FindLast(const String&in, uint start = 0xffffffff, bool caseSensitive = true) const", asMETHODPR(String, FindLast, (const String&, unsigned, bool) const, unsigned), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "uint FindLast(uint8, uint start = 0xffffffff, bool caseSensitive = true) const", asMETHODPR(String, FindLast, (char, unsigned, bool) const, unsigned), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "bool StartsWith(const String&in, bool caseSensitive = true) const", asMETHOD(String, StartsWith), asCALL_THISCALL);
+    engine->RegisterObjectMethod("String", "bool EndsWith(const String&in, bool caseSensitive = true) const", asMETHOD(String, EndsWith), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "String Substring(uint) const", asMETHODPR(String, Substring, (unsigned) const, String), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "String Substring(uint) const", asMETHODPR(String, Substring, (unsigned) const, String), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "String Substring(uint, uint) const", asMETHODPR(String, Substring, (unsigned, unsigned) const, String), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "String Substring(uint, uint) const", asMETHODPR(String, Substring, (unsigned, unsigned) const, String), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "String ToUpper() const", asMETHOD(String, ToUpper), asCALL_THISCALL);
     engine->RegisterObjectMethod("String", "String ToUpper() const", asMETHOD(String, ToUpper), asCALL_THISCALL);

+ 2 - 2
Source/Engine/Script/IOAPI.cpp

@@ -330,8 +330,8 @@ void RegisterFileSystem(asIScriptEngine* engine)
 
 
     engine->RegisterGlobalFunction("String GetPath(const String&in)", asFUNCTION(GetPath), asCALL_CDECL);
     engine->RegisterGlobalFunction("String GetPath(const String&in)", asFUNCTION(GetPath), asCALL_CDECL);
     engine->RegisterGlobalFunction("String GetFileName(const String&in)", asFUNCTION(GetFileName), asCALL_CDECL);
     engine->RegisterGlobalFunction("String GetFileName(const String&in)", asFUNCTION(GetFileName), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String GetExtension(const String&in)", asFUNCTION(GetExtension), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String GetFileNameAndExtension(const String&in)", asFUNCTION(GetFileNameAndExtension), asCALL_CDECL);
+    engine->RegisterGlobalFunction("String GetExtension(const String&in, bool lowercaseExtension = true)", asFUNCTION(GetExtension), asCALL_CDECL);
+    engine->RegisterGlobalFunction("String GetFileNameAndExtension(const String&in, bool lowercaseExtension = false)", asFUNCTION(GetFileNameAndExtension), asCALL_CDECL);
     engine->RegisterGlobalFunction("String ReplaceExtension(const String&in, const String&in)", asFUNCTION(ReplaceExtension), asCALL_CDECL);
     engine->RegisterGlobalFunction("String ReplaceExtension(const String&in, const String&in)", asFUNCTION(ReplaceExtension), asCALL_CDECL);
     engine->RegisterGlobalFunction("String AddTrailingSlash(const String&in)", asFUNCTION(AddTrailingSlash), asCALL_CDECL);
     engine->RegisterGlobalFunction("String AddTrailingSlash(const String&in)", asFUNCTION(AddTrailingSlash), asCALL_CDECL);
     engine->RegisterGlobalFunction("String RemoveTrailingSlash(const String&in)", asFUNCTION(RemoveTrailingSlash), asCALL_CDECL);
     engine->RegisterGlobalFunction("String RemoveTrailingSlash(const String&in)", asFUNCTION(RemoveTrailingSlash), asCALL_CDECL);

+ 1 - 0
Source/Engine/Script/MathAPI.cpp

@@ -703,6 +703,7 @@ static void RegisterRay(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Ray", "float HitDistance(const BoundingBox&in) const", asMETHODPR(Ray, HitDistance, (const BoundingBox&) const, float), asCALL_THISCALL);
     engine->RegisterObjectMethod("Ray", "float HitDistance(const BoundingBox&in) const", asMETHODPR(Ray, HitDistance, (const BoundingBox&) const, float), asCALL_THISCALL);
     engine->RegisterObjectMethod("Ray", "float HitDistance(const Frustum&in, bool solidInside = true) const", asMETHODPR(Ray, HitDistance, (const Frustum&, bool) const, float), asCALL_THISCALL);
     engine->RegisterObjectMethod("Ray", "float HitDistance(const Frustum&in, bool solidInside = true) const", asMETHODPR(Ray, HitDistance, (const Frustum&, bool) const, float), asCALL_THISCALL);
     engine->RegisterObjectMethod("Ray", "float HitDistance(const Vector3&in, const Vector3&in, const Vector3&in) const", asMETHODPR(Ray, HitDistance, (const Vector3&, const Vector3&, const Vector3&) const, float), asCALL_THISCALL);
     engine->RegisterObjectMethod("Ray", "float HitDistance(const Vector3&in, const Vector3&in, const Vector3&in) const", asMETHODPR(Ray, HitDistance, (const Vector3&, const Vector3&, const Vector3&) const, float), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Ray", "Ray Transformed(const Matrix3x4&in) const", asMETHOD(Ray, Transformed), asCALL_THISCALL);
     engine->RegisterObjectProperty("Ray", "Vector3 origin", offsetof(Ray, origin_));
     engine->RegisterObjectProperty("Ray", "Vector3 origin", offsetof(Ray, origin_));
     engine->RegisterObjectProperty("Ray", "Vector3 direction", offsetof(Ray, direction_));
     engine->RegisterObjectProperty("Ray", "Vector3 direction", offsetof(Ray, direction_));
 }
 }

+ 1 - 1
Source/Engine/UI/Font.cpp

@@ -178,7 +178,7 @@ bool Font::Load(Deserializer& source)
         return false;
         return false;
     }
     }
 
 
-    String ext = GetExtension(GetName()).ToLower();
+    String ext = GetExtension(GetName());
     if (ext == ".ttf")
     if (ext == ".ttf")
         fontType_ = FONT_TTF;
         fontType_ = FONT_TTF;
     else if (ext == ".xml" || ext == ".fnt")
     else if (ext == ".xml" || ext == ".fnt")

+ 13 - 13
Source/Extras/LuaScript/pkgs/Container/Str.pkg

@@ -15,15 +15,15 @@ class String
     bool operator == (const char* rhs) const;
     bool operator == (const char* rhs) const;
     tolua_outside bool StringEquals @ Equals(const String& rhs) const;
     tolua_outside bool StringEquals @ Equals(const String& rhs) const;
     tolua_outside bool StringEquals @ Equals(const char*& rhs) const;
     tolua_outside bool StringEquals @ Equals(const char*& rhs) const;
-    
-    void Replace(const String& replaceThis, const String& replaceWith);
-    void Replace(const char* replaceThis, const char* replaceWith);
+
+    void Replace(const String& replaceThis, const String& replaceWith, bool caseSensitive = true);
+    void Replace(const char* replaceThis, const char* replaceWith, bool caseSensitive = true);
     
     
     void Replace(unsigned pos, unsigned length, const String& replaceWith);
     void Replace(unsigned pos, unsigned length, const String& replaceWith);
     void Replace(unsigned pos, unsigned length, const char* replaceWith);
     void Replace(unsigned pos, unsigned length, const char* replaceWith);
     
     
-    String Replaced(const String& replaceThis, const String& replaceWith) const;
-    String Replaced(const char* replaceThis, const char* replaceWith) const;
+    String Replaced(const String& replaceThis, const String& replaceWith, bool caseSensitive = true) const;
+    String Replaced(const char* replaceThis, const char* replaceWith, bool caseSensitive = true) const;
     
     
     String& Append(const String& str);
     String& Append(const String& str);
     String& Append(const char* str);
     String& Append(const char* str);
@@ -44,16 +44,16 @@ class String
     String ToUpper() const;
     String ToUpper() const;
     String ToLower() const;
     String ToLower() const;
     
     
-    unsigned Find(const String& str, unsigned startPos = 0) const;
-    unsigned Find(const char* str, unsigned startPos = 0) const;
+    unsigned Find(const String& str, unsigned startPos = 0, bool caseSensitive = true) const;
+    unsigned Find(const char* str, unsigned startPos = 0, bool caseSensitive = true) const;
     
     
-    unsigned FindLast(const String& str, unsigned startPos = String::NPOS) const;
-    unsigned FindLast(const char* str, unsigned startPos = String::NPOS) const;
+    unsigned FindLast(const String& str, unsigned startPos = String::NPOS, bool caseSensitive = true) const;
+    unsigned FindLast(const char* str, unsigned startPos = String::NPOS, bool caseSensitive = true) const;
     
     
-    bool StartsWith(const String& str) const;
-    bool StartsWith(const char* str) const;
-    bool EndsWith(const String& str) const;
-    bool EndsWith(const char* str) const;
+    bool StartsWith(const String& str, bool caseSensitive = true) const;
+    bool StartsWith(const char* str, bool caseSensitive = true) const;
+    bool EndsWith(const String& str, bool caseSensitive = true) const;
+    bool EndsWith(const char* str, bool caseSensitive = true) const;
     
     
     const char* CString() const;
     const char* CString() const;
     unsigned Length() const;
     unsigned Length() const;

+ 3 - 3
Source/Extras/LuaScript/pkgs/IO/FileSystem.pkg

@@ -35,7 +35,7 @@ class FileSystem : public Object
     
     
     String GetCurrentDir() const;
     String GetCurrentDir() const;
     bool HasRegisteredPaths() const;
     bool HasRegisteredPaths() const;
-    
+
     bool CheckAccess(const String& pathName) const;
     bool CheckAccess(const String& pathName) const;
     bool CheckAccess(const char* pathName) const;
     bool CheckAccess(const char* pathName) const;
     
     
@@ -58,8 +58,8 @@ class FileSystem : public Object
 
 
 String GetPath(const String& fullPath);
 String GetPath(const String& fullPath);
 String GetFileName(const String& fullPath);
 String GetFileName(const String& fullPath);
-String GetExtension(const String& fullPath);
-String GetFileNameAndExtension(const String& fullPath);
+String GetExtension(const String& fullPath, bool lowercaseExtension = true);
+String GetFileNameAndExtension(const String& fullPath, bool lowercaseExtension = false);
 String ReplaceExtension(const String& fullPath, const String& newExtension);
 String ReplaceExtension(const String& fullPath, const String& newExtension);
 String AddTrailingSlash(const String& pathName);
 String AddTrailingSlash(const String& pathName);
 String RemoveTrailingSlash(const String& pathName);
 String RemoveTrailingSlash(const String& pathName);

+ 2 - 0
Source/Extras/LuaScript/pkgs/Math/Ray.pkg

@@ -22,6 +22,8 @@ class Ray
     float HitDistance(const Sphere& sphere) const;
     float HitDistance(const Sphere& sphere) const;
     float HitDistance(const Vector3& v0, const Vector3& v1, const Vector3& v2) const;
     float HitDistance(const Vector3& v0, const Vector3& v1, const Vector3& v2) const;
     
     
+    Ray Transformed(const Matrix3x4& transform) const;
+    
     Vector3 origin_ @ origin;
     Vector3 origin_ @ origin;
     Vector3 direction_ @ direction;
     Vector3 direction_ @ direction;
 };
 };

+ 1 - 2
Source/Tools/AssetImporter/AssetImporter.cpp

@@ -320,8 +320,7 @@ void Run(const Vector<String>& arguments)
             // If output file already has the Models/ path (model mode), do not take it into the resource path
             // If output file already has the Models/ path (model mode), do not take it into the resource path
             if (command == "model")
             if (command == "model")
             {
             {
-                String resPathLower = resourcePath_.ToLower();
-                if (resPathLower.FindLast("models/") == resPathLower.Length() - 7)
+                if (resourcePath_.EndsWith("Models/", false))
                     resourcePath_ = resourcePath_.Substring(0, resourcePath_.Length() - 7);
                     resourcePath_ = resourcePath_.Substring(0, resourcePath_.Length() - 7);
             }
             }
             if (resourcePath_.Empty())
             if (resourcePath_.Empty())

+ 1 - 1
Source/Tools/Urho3D/Urho3D.cpp

@@ -136,7 +136,7 @@ void Urho::Setup()
 void Urho::Start()
 void Urho::Start()
 {
 {
 #ifdef ENABLE_LUA
 #ifdef ENABLE_LUA
-    String extension = GetExtension(scriptFileName_).ToLower();
+    String extension = GetExtension(scriptFileName_);
     if (extension != ".lua")
     if (extension != ".lua")
     {
     {
 #endif
 #endif