Explorar el Código

Merge remote-tracking branch 'upstream/master'

Lumak hace 9 años
padre
commit
1a266d6b89

+ 2 - 2
CMake/Modules/FindUrho3D.cmake

@@ -170,7 +170,7 @@ else ()
     set (URHO3D_LIB_TYPE_SAVED ${URHO3D_LIB_TYPE})  # We need this to reset the auto-discovered URHO3D_LIB_TYPE variable before looping
     set (URHO3D_LIB_TYPE_SAVED ${URHO3D_LIB_TYPE})  # We need this to reset the auto-discovered URHO3D_LIB_TYPE variable before looping
     foreach (ABI_64BIT RANGE ${URHO3D_64BIT} 0)
     foreach (ABI_64BIT RANGE ${URHO3D_64BIT} 0)
         # Break if the compiler is not multilib-capable and the ABI is not its native
         # Break if the compiler is not multilib-capable and the ABI is not its native
-        if ((MSVC OR MINGW OR ANDROID OR ARM OR WEB) AND NOT ABI_64BIT EQUAL NATIVE_64BIT)
+        if ((MSVC OR ANDROID OR ARM OR WEB) AND NOT ABI_64BIT EQUAL NATIVE_64BIT)
             break ()
             break ()
         endif ()
         endif ()
         # Set to search in 'lib' or 'lib64' based on the ABI being tested
         # Set to search in 'lib' or 'lib64' based on the ABI being tested
@@ -223,7 +223,7 @@ else ()
         if (URHO3D_COMPILE_RESULT)
         if (URHO3D_COMPILE_RESULT)
             break ()    # Use the cached result instead of redoing try_run() each time
             break ()    # Use the cached result instead of redoing try_run() each time
         elseif (URHO3D_LIBRARIES)
         elseif (URHO3D_LIBRARIES)
-            if (NOT (MSVC OR MINGW OR ANDROID OR ARM OR WEB OR XCODE) AND NOT ABI_64BIT)
+            if (NOT (MSVC OR ANDROID OR ARM OR WEB OR XCODE) AND NOT ABI_64BIT)
                 set (COMPILER_32BIT_FLAG -m32)
                 set (COMPILER_32BIT_FLAG -m32)
             endif ()
             endif ()
             # Below variables are loop invariant but there is no harm to keep them here
             # Below variables are loop invariant but there is no harm to keep them here

+ 1 - 1
CMake/Modules/Urho3D-CMake-common.cmake

@@ -98,7 +98,7 @@ set (CMAKE_EXE_LINKER_FLAGS "${INDIRECT_DEPS_EXE_LINKER_FLAGS} ${CMAKE_EXE_LINKE
 include (CMakeDependentOption)
 include (CMakeDependentOption)
 option (URHO3D_C++11 "Enable C++11 standard")
 option (URHO3D_C++11 "Enable C++11 standard")
 cmake_dependent_option (IOS "Setup build for iOS platform" FALSE "XCODE" FALSE)
 cmake_dependent_option (IOS "Setup build for iOS platform" FALSE "XCODE" FALSE)
-cmake_dependent_option (URHO3D_64BIT "Enable 64-bit build, the default is set based on the native ABI of the chosen compiler toolchain" ${NATIVE_64BIT} "NOT MSVC AND NOT MINGW AND NOT ANDROID AND NOT (ARM AND NOT IOS) AND NOT WEB AND NOT POWERPC" ${NATIVE_64BIT})     # Intentionally only enable the option for iOS but not for tvOS as the latter is 64-bit only
+cmake_dependent_option (URHO3D_64BIT "Enable 64-bit build, the default is set based on the native ABI of the chosen compiler toolchain" ${NATIVE_64BIT} "NOT MSVC AND NOT ANDROID AND NOT (ARM AND NOT IOS) AND NOT WEB AND NOT POWERPC" ${NATIVE_64BIT})     # Intentionally only enable the option for iOS but not for tvOS as the latter is 64-bit only
 option (URHO3D_ANGELSCRIPT "Enable AngelScript scripting support" TRUE)
 option (URHO3D_ANGELSCRIPT "Enable AngelScript scripting support" TRUE)
 option (URHO3D_LUA "Enable additional Lua scripting support" TRUE)
 option (URHO3D_LUA "Enable additional Lua scripting support" TRUE)
 option (URHO3D_NAVIGATION "Enable navigation support" TRUE)
 option (URHO3D_NAVIGATION "Enable navigation support" TRUE)

+ 6 - 0
Docs/AngelScriptAPI.h

@@ -11316,6 +11316,7 @@ WrapMode GetAttributeAnimationWrapMode(const String&) const;
 Variant GetAttributeDefault(const String&) const;
 Variant GetAttributeDefault(const String&) const;
 float GetHeight(const Vector3&) const;
 float GetHeight(const Vector3&) const;
 bool GetInterceptNetworkUpdate(const String&) const;
 bool GetInterceptNetworkUpdate(const String&) const;
+TerrainPatch GetNeighborPatch(int, int) const;
 Vector3 GetNormal(const Vector3&) const;
 Vector3 GetNormal(const Vector3&) const;
 TerrainPatch GetPatch(int, int) const;
 TerrainPatch GetPatch(int, int) const;
 bool HasSubscribedToEvent(Object, const String&);
 bool HasSubscribedToEvent(Object, const String&);
@@ -11342,6 +11343,7 @@ void SetAttributeAnimationSpeed(const String&, float);
 void SetAttributeAnimationTime(const String&, float);
 void SetAttributeAnimationTime(const String&, float);
 void SetAttributeAnimationWrapMode(const String&, WrapMode);
 void SetAttributeAnimationWrapMode(const String&, WrapMode);
 void SetInterceptNetworkUpdate(const String&, bool);
 void SetInterceptNetworkUpdate(const String&, bool);
+void SetNeighbors(Terrain, Terrain, Terrain, Terrain);
 IntVector2 WorldToHeightMap(const Vector3&) const;
 IntVector2 WorldToHeightMap(const Vector3&) const;
 
 
 // Properties:
 // Properties:
@@ -11355,6 +11357,7 @@ bool castShadows;
 /* readonly */
 /* readonly */
 String category;
 String category;
 float drawDistance;
 float drawDistance;
+Terrain eastNeighbor;
 bool enabled;
 bool enabled;
 /* readonly */
 /* readonly */
 bool enabledEffective;
 bool enabledEffective;
@@ -11368,6 +11371,7 @@ uint maxLights;
 uint maxLodLevels;
 uint maxLodLevels;
 /* readonly */
 /* readonly */
 Node node;
 Node node;
+Terrain northNeighbor;
 /* readonly */
 /* readonly */
 uint numAttributes;
 uint numAttributes;
 /* readonly */
 /* readonly */
@@ -11386,6 +11390,7 @@ int refs;
 float shadowDistance;
 float shadowDistance;
 uint shadowMask;
 uint shadowMask;
 bool smoothing;
 bool smoothing;
+Terrain southNeighbor;
 Vector3 spacing;
 Vector3 spacing;
 bool temporary;
 bool temporary;
 /* readonly */
 /* readonly */
@@ -11395,6 +11400,7 @@ String typeName;
 uint viewMask;
 uint viewMask;
 /* readonly */
 /* readonly */
 int weakRefs;
 int weakRefs;
+Terrain westNeighbor;
 uint zoneMask;
 uint zoneMask;
 };
 };
 
 

+ 14 - 0
Docs/LuaScriptAPI.dox

@@ -6080,6 +6080,11 @@ Methods:
 - void SetSmoothing(bool enable)
 - void SetSmoothing(bool enable)
 - bool SetHeightMap(Image* image)
 - bool SetHeightMap(Image* image)
 - void SetMaterial(Material* material)
 - void SetMaterial(Material* material)
+- void SetNorthNeighbor(Terrain* north)
+- void SetSouthNeighbor(Terrain* south)
+- void SetWestNeighbor(Terrain* west)
+- void SetEastNeighbor(Terrain* east)
+- void SetNeighbors(Terrain* north, Terrain* south, Terrain* west, Terrain* east)
 - void SetDrawDistance(float distance)
 - void SetDrawDistance(float distance)
 - void SetShadowDistance(float distance)
 - void SetShadowDistance(float distance)
 - void SetLodBias(float bias)
 - void SetLodBias(float bias)
@@ -6101,8 +6106,13 @@ Methods:
 - bool GetSmoothing() const
 - bool GetSmoothing() const
 - Image* GetHeightMap() const
 - Image* GetHeightMap() const
 - Material* GetMaterial() const
 - Material* GetMaterial() const
+- Terrain* GetNorthNeighbor() const
+- Terrain* GetSouthNeighbor() const
+- Terrain* GetWestNeighbor() const
+- Terrain* GetEastNeighbor() const
 - TerrainPatch* GetPatch(unsigned index) const
 - TerrainPatch* GetPatch(unsigned index) const
 - TerrainPatch* GetPatch(int x, int z) const
 - TerrainPatch* GetPatch(int x, int z) const
+- TerrainPatch* GetNeighborPatch(int x, int z) const
 - float GetHeight(const Vector3& worldPosition) const
 - float GetHeight(const Vector3& worldPosition) const
 - Vector3 GetNormal(const Vector3& worldPosition) const
 - Vector3 GetNormal(const Vector3& worldPosition) const
 - IntVector2 WorldToHeightMap(const Vector3& worldPosition) const
 - IntVector2 WorldToHeightMap(const Vector3& worldPosition) const
@@ -6131,6 +6141,10 @@ Properties:
 - bool smoothing
 - bool smoothing
 - Image* heightMap
 - Image* heightMap
 - Material* material
 - Material* material
+- Terrain* northNeighbor
+- Terrain* southNeighbor
+- Terrain* westNeighbor
+- Terrain* eastNeighbor
 - float drawDistance
 - float drawDistance
 - float shadowDistance
 - float shadowDistance
 - float lodBias
 - float lodBias

+ 10 - 0
Docs/ScriptAPI.dox

@@ -2050,6 +2050,10 @@ namespace Urho3D
 - %Is %Enabled : bool
 - %Is %Enabled : bool
 - %Height %Map : ResourceRef
 - %Height %Map : ResourceRef
 - %Material : ResourceRef
 - %Material : ResourceRef
+- %North %Neighbor %NodeID : int
+- %South %Neighbor %NodeID : int
+- %West %Neighbor %NodeID : int
+- %East %Neighbor %NodeID : int
 - %Vertex %Spacing : Vector3
 - %Vertex %Spacing : Vector3
 - %Patch %Size : int
 - %Patch %Size : int
 - %Max %LOD %Levels : int
 - %Max %LOD %Levels : int
@@ -12559,6 +12563,7 @@ Methods:
 - Variant GetAttributeDefault(const String&) const
 - Variant GetAttributeDefault(const String&) const
 - float GetHeight(const Vector3&) const
 - float GetHeight(const Vector3&) const
 - bool GetInterceptNetworkUpdate(const String&) const
 - bool GetInterceptNetworkUpdate(const String&) const
+- TerrainPatch@ GetNeighborPatch(int, int) const
 - Vector3 GetNormal(const Vector3&) const
 - Vector3 GetNormal(const Vector3&) const
 - TerrainPatch@ GetPatch(int, int) const
 - TerrainPatch@ GetPatch(int, int) const
 - bool HasSubscribedToEvent(Object@, const String&)
 - bool HasSubscribedToEvent(Object@, const String&)
@@ -12585,6 +12590,7 @@ Methods:
 - void SetAttributeAnimationTime(const String&, float)
 - void SetAttributeAnimationTime(const String&, float)
 - void SetAttributeAnimationWrapMode(const String&, WrapMode)
 - void SetAttributeAnimationWrapMode(const String&, WrapMode)
 - void SetInterceptNetworkUpdate(const String&, bool)
 - void SetInterceptNetworkUpdate(const String&, bool)
+- void SetNeighbors(Terrain@, Terrain@, Terrain@, Terrain@)
 - IntVector2 WorldToHeightMap(const Vector3&) const
 - IntVector2 WorldToHeightMap(const Vector3&) const
 
 
 Properties:
 Properties:
@@ -12596,6 +12602,7 @@ Properties:
 - bool castShadows
 - bool castShadows
 - String category // readonly
 - String category // readonly
 - float drawDistance
 - float drawDistance
+- Terrain@ eastNeighbor
 - bool enabled
 - bool enabled
 - bool enabledEffective // readonly
 - bool enabledEffective // readonly
 - Image@ heightMap
 - Image@ heightMap
@@ -12606,6 +12613,7 @@ Properties:
 - uint maxLights
 - uint maxLights
 - uint maxLodLevels
 - uint maxLodLevels
 - Node@ node // readonly
 - Node@ node // readonly
+- Terrain@ northNeighbor
 - uint numAttributes // readonly
 - uint numAttributes // readonly
 - IntVector2 numPatches // readonly
 - IntVector2 numPatches // readonly
 - IntVector2 numVertices // readonly
 - IntVector2 numVertices // readonly
@@ -12619,12 +12627,14 @@ Properties:
 - float shadowDistance
 - float shadowDistance
 - uint shadowMask
 - uint shadowMask
 - bool smoothing
 - bool smoothing
+- Terrain@ southNeighbor
 - Vector3 spacing
 - Vector3 spacing
 - bool temporary
 - bool temporary
 - StringHash type // readonly
 - StringHash type // readonly
 - String typeName // readonly
 - String typeName // readonly
 - uint viewMask
 - uint viewMask
 - int weakRefs // readonly
 - int weakRefs // readonly
+- Terrain@ westNeighbor
 - uint zoneMask
 - uint zoneMask
 
 
 <a name="Class_TerrainPatch"></a>
 <a name="Class_TerrainPatch"></a>

+ 6 - 1
Docs/Urho3D.dox

@@ -121,6 +121,7 @@ Urho3D development, contributions and bugfixes by:
 - MonkeyFirst
 - MonkeyFirst
 - Newb I the Newbd
 - Newb I the Newbd
 - OvermindDL1
 - OvermindDL1
+- Scellow
 - Skrylar
 - Skrylar
 - TheComet93
 - TheComet93
 - Y-way
 - Y-way
@@ -204,7 +205,11 @@ Jack and mushroom models from the realXtend project. (https://www.realxtend.org)
 Ninja model and terrain, water, smoke, flare and status bar textures from OGRE.<br>
 Ninja model and terrain, water, smoke, flare and status bar textures from OGRE.<br>
 BlueHighway font from Larabie Fonts.<br>
 BlueHighway font from Larabie Fonts.<br>
 Anonymous Pro font by Mark Simonson.<br>
 Anonymous Pro font by Mark Simonson.<br>
-NinjaSnowWar sounds by Veli-Pekka T&auml;til&auml;.
+NinjaSnowWar sounds by Veli-Pekka T&auml;til&auml;.<br>
+PBR textures from Substance Share. (https://share.allegorithmic.com)<br>
+IBL textures from HDRLab's sIBL Archive.<br>
+Dieselpunk Moto model by allexandr007.<br>
+Mutant model from Mixamo.<br>
 
 
 
 
 \page License License
 \page License License

+ 5 - 0
README.md

@@ -73,6 +73,7 @@ Urho3D development, contributions and bugfixes by:
 - MonkeyFirst
 - MonkeyFirst
 - Newb I the Newbd
 - Newb I the Newbd
 - OvermindDL1
 - OvermindDL1
+- Scellow
 - Skrylar
 - Skrylar
 - TheComet93
 - TheComet93
 - Y-way
 - Y-way
@@ -167,6 +168,10 @@ Ninja model and terrain, water, smoke, flare and status bar textures from OGRE.
 BlueHighway font from Larabie Fonts.
 BlueHighway font from Larabie Fonts.
 Anonymous Pro font by Mark Simonson.
 Anonymous Pro font by Mark Simonson.
 NinjaSnowWar sounds by Veli-Pekka Tätilä.
 NinjaSnowWar sounds by Veli-Pekka Tätilä.
+PBR textures from Substance Share. (https://share.allegorithmic.com)
+IBL textures from HDRLab's sIBL Archive.
+Dieselpunk Moto model by allexandr007.
+Mutant model from Mixamo.
 
 
 ##Documentation
 ##Documentation
 Urho3D classes have been sparsely documented using Doxygen notation. To
 Urho3D classes have been sparsely documented using Doxygen notation. To

+ 2 - 2
Source/Samples/18_CharacterDemo/Character.cpp

@@ -151,10 +151,10 @@ void Character::HandleNodeCollision(StringHash eventType, VariantMap& eventData)
         /*float contactDistance = */contacts.ReadFloat();
         /*float contactDistance = */contacts.ReadFloat();
         /*float contactImpulse = */contacts.ReadFloat();
         /*float contactImpulse = */contacts.ReadFloat();
 
 
-        // If contact is below node center and mostly vertical, assume it's a ground contact
+        // If contact is below node center and pointing up, assume it's a ground contact
         if (contactPosition.y_ < (node_->GetPosition().y_ + 1.0f))
         if (contactPosition.y_ < (node_->GetPosition().y_ + 1.0f))
         {
         {
-            float level = Abs(contactNormal.y_);
+            float level = contactNormal.y_;
             if (level > 0.75)
             if (level > 0.75)
                 onGround_ = true;
                 onGround_ = true;
         }
         }

+ 1 - 1
Source/Urho3D/.soversion

@@ -1 +1 @@
-0.0.251
+0.0.252

+ 10 - 0
Source/Urho3D/AngelScript/GraphicsAPI.cpp

@@ -1728,7 +1728,9 @@ static void RegisterTerrain(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Terrain", "float GetHeight(const Vector3&in) const", asMETHOD(Terrain, GetHeight), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "float GetHeight(const Vector3&in) const", asMETHOD(Terrain, GetHeight), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "Vector3 GetNormal(const Vector3&in) const", asMETHOD(Terrain, GetNormal), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "Vector3 GetNormal(const Vector3&in) const", asMETHOD(Terrain, GetNormal), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "TerrainPatch@+ GetPatch(int, int) const", asMETHODPR(Terrain, GetPatch, (int, int) const, TerrainPatch*), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "TerrainPatch@+ GetPatch(int, int) const", asMETHODPR(Terrain, GetPatch, (int, int) const, TerrainPatch*), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "TerrainPatch@+ GetNeighborPatch(int, int) const", asMETHODPR(Terrain, GetNeighborPatch, (int, int) const, TerrainPatch*), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "IntVector2 WorldToHeightMap(const Vector3&in) const", asMETHOD(Terrain, WorldToHeightMap), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "IntVector2 WorldToHeightMap(const Vector3&in) const", asMETHOD(Terrain, WorldToHeightMap), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "void SetNeighbors(Terrain@+, Terrain@+, Terrain@+, Terrain@+)", asMETHOD(Terrain, SetNeighbors), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "void set_material(Material@+)", asMETHOD(Terrain, SetMaterial), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "void set_material(Material@+)", asMETHOD(Terrain, SetMaterial), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "Material@+ get_material() const", asMETHOD(Terrain, GetMaterial), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "Material@+ get_material() const", asMETHOD(Terrain, GetMaterial), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "void set_maxLodLevels(uint)", asMETHOD(Terrain, SetMaxLodLevels), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "void set_maxLodLevels(uint)", asMETHOD(Terrain, SetMaxLodLevels), asCALL_THISCALL);
@@ -1768,6 +1770,14 @@ static void RegisterTerrain(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Terrain", "uint get_zoneMask() const", asMETHOD(Terrain, GetZoneMask), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "uint get_zoneMask() const", asMETHOD(Terrain, GetZoneMask), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "void set_maxLights(uint)", asMETHOD(Terrain, SetMaxLights), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "void set_maxLights(uint)", asMETHOD(Terrain, SetMaxLights), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "uint get_maxLights() const", asMETHOD(Terrain, GetMaxLights), asCALL_THISCALL);
     engine->RegisterObjectMethod("Terrain", "uint get_maxLights() const", asMETHOD(Terrain, GetMaxLights), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "void set_northNeighbor(Terrain@+)", asMETHOD(Terrain, SetNorthNeighbor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "Terrain@+ get_northNeighbor() const", asMETHOD(Terrain, GetNorthNeighbor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "void set_southNeighbor(Terrain@+)", asMETHOD(Terrain, SetSouthNeighbor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "Terrain@+ get_southNeighbor() const", asMETHOD(Terrain, GetSouthNeighbor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "void set_westNeighbor(Terrain@+)", asMETHOD(Terrain, SetEastNeighbor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "Terrain@+ get_westNeighbor() const", asMETHOD(Terrain, GetEastNeighbor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "void set_eastNeighbor(Terrain@+)", asMETHOD(Terrain, SetWestNeighbor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Terrain", "Terrain@+ get_eastNeighbor() const", asMETHOD(Terrain, GetWestNeighbor), asCALL_THISCALL);
 }
 }
 
 
 
 

+ 4 - 0
Source/Urho3D/CMakeLists.txt

@@ -268,6 +268,10 @@ if (URHO3D_LUA)
     # This is more practical than patching its header files in many places to make them work with relative path
     # This is more practical than patching its header files in many places to make them work with relative path
     list (APPEND INCLUDE_DIRS ${CMAKE_BINARY_DIR}/${DEST_INCLUDE_DIR}/ThirdParty/Lua${JIT})
     list (APPEND INCLUDE_DIRS ${CMAKE_BINARY_DIR}/${DEST_INCLUDE_DIR}/ThirdParty/Lua${JIT})
 endif ()
 endif ()
+# Workaround for GCC 5.4 and above when building a SHARED lib type for Linux platform to fix the undefined symbol "__cpu_model" issue (see #1519)
+if (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.3.1 AND CMAKE_SYSTEM_NAME STREQUAL Linux AND URHO3D_LIB_TYPE STREQUAL SHARED)  # 5.3.1 was the last known good version
+    list (APPEND LIBS gcc)
+endif ()
 
 
 # Setup library output path
 # Setup library output path
 if (ANDROID)
 if (ANDROID)

+ 193 - 7
Source/Urho3D/Graphics/Terrain.cpp

@@ -105,7 +105,12 @@ Terrain::Terrain(Context* context) :
     shadowDistance_(0.0f),
     shadowDistance_(0.0f),
     lodBias_(1.0f),
     lodBias_(1.0f),
     maxLights_(0),
     maxLights_(0),
-    recreateTerrain_(false)
+    northID_(0),
+    southID_(0),
+    westID_(0),
+    eastID_(0),
+    recreateTerrain_(false),
+    neighborsDirty_(false)
 {
 {
     indexBuffer_->SetShadowed(true);
     indexBuffer_->SetShadowed(true);
 }
 }
@@ -123,6 +128,10 @@ void Terrain::RegisterObject(Context* context)
         AM_DEFAULT);
         AM_DEFAULT);
     URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
     URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()),
         AM_DEFAULT);
         AM_DEFAULT);
+    URHO3D_ATTRIBUTE("North Neighbor NodeID", unsigned, northID_, 0, AM_DEFAULT | AM_NODEID);
+    URHO3D_ATTRIBUTE("South Neighbor NodeID", unsigned, southID_, 0, AM_DEFAULT | AM_NODEID);
+    URHO3D_ATTRIBUTE("West Neighbor NodeID", unsigned, westID_, 0, AM_DEFAULT | AM_NODEID);
+    URHO3D_ATTRIBUTE("East Neighbor NodeID", unsigned, eastID_, 0, AM_DEFAULT | AM_NODEID);
     URHO3D_ATTRIBUTE("Vertex Spacing", Vector3, spacing_, DEFAULT_SPACING, AM_DEFAULT);
     URHO3D_ATTRIBUTE("Vertex Spacing", Vector3, spacing_, DEFAULT_SPACING, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Patch Size", GetPatchSize, SetPatchSizeAttr, int, DEFAULT_PATCH_SIZE, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Patch Size", GetPatchSize, SetPatchSizeAttr, int, DEFAULT_PATCH_SIZE, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Max LOD Levels", GetMaxLodLevels, SetMaxLodLevelsAttr, unsigned, MAX_LOD_LEVELS, AM_DEFAULT);
     URHO3D_ACCESSOR_ATTRIBUTE("Max LOD Levels", GetMaxLodLevels, SetMaxLodLevelsAttr, unsigned, MAX_LOD_LEVELS, AM_DEFAULT);
@@ -145,15 +154,35 @@ void Terrain::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
 {
 {
     Serializable::OnSetAttribute(attr, src);
     Serializable::OnSetAttribute(attr, src);
 
 
-    // Change of any non-accessor attribute requires recreation of the terrain
+    // Change of any non-accessor attribute requires recreation of the terrain, or setting the neighbor terrains
     if (!attr.accessor_)
     if (!attr.accessor_)
-        recreateTerrain_ = true;
+    {
+        if (attr.mode_ & AM_NODEID)
+            neighborsDirty_ = true;
+        else
+            recreateTerrain_ = true;
+    }
 }
 }
 
 
 void Terrain::ApplyAttributes()
 void Terrain::ApplyAttributes()
 {
 {
     if (recreateTerrain_)
     if (recreateTerrain_)
         CreateGeometry();
         CreateGeometry();
+    
+    if (neighborsDirty_)
+    {
+        Scene* scene = GetScene();
+        Node* north = scene ? scene->GetNode(northID_) : (Node*)0;
+        Node* south = scene ? scene->GetNode(southID_) : (Node*)0;
+        Node* west = scene ? scene->GetNode(westID_) : (Node*)0;
+        Node* east = scene ? scene->GetNode(eastID_) : (Node*)0;
+        Terrain* northTerrain = north ? north->GetComponent<Terrain>() : (Terrain*)0;
+        Terrain* southTerrain = south ? south->GetComponent<Terrain>() : (Terrain*)0;
+        Terrain* westTerrain = west ? west->GetComponent<Terrain>() : (Terrain*)0;
+        Terrain* eastTerrain = east ? east->GetComponent<Terrain>() : (Terrain*)0;
+        SetNeighbors(northTerrain, southTerrain, westTerrain, eastTerrain);
+        neighborsDirty_ = false;
+    }
 }
 }
 
 
 void Terrain::OnSetEnabled()
 void Terrain::OnSetEnabled()
@@ -248,6 +277,122 @@ void Terrain::SetMaterial(Material* material)
     MarkNetworkUpdate();
     MarkNetworkUpdate();
 }
 }
 
 
+void Terrain::SetNorthNeighbor(Terrain* north)
+{
+    if (north == north_)
+        return;
+
+    if (north_ && north_->GetNode())
+        UnsubscribeFromEvent(north_->GetNode(), E_TERRAINCREATED);
+
+    north_ = north;
+    if (north_ && north_->GetNode())
+    {
+        northID_ = north_->GetNode()->GetID();
+        SubscribeToEvent(north_->GetNode(), E_TERRAINCREATED, URHO3D_HANDLER(Terrain, HandleNeighborTerrainCreated));
+    }
+
+    UpdateEdgePatchNeighbors();
+    MarkNetworkUpdate();
+}
+
+void Terrain::SetSouthNeighbor(Terrain* south)
+{
+    if (south == south_)
+        return;
+
+    if (south_ && south_->GetNode())
+        UnsubscribeFromEvent(south_->GetNode(), E_TERRAINCREATED);
+
+    south_ = south;
+    if (south_ && south_->GetNode())
+    {
+        southID_ = south_->GetNode()->GetID();
+        SubscribeToEvent(south_->GetNode(), E_TERRAINCREATED, URHO3D_HANDLER(Terrain, HandleNeighborTerrainCreated));
+    }
+
+    UpdateEdgePatchNeighbors();
+    MarkNetworkUpdate();
+}
+
+void Terrain::SetWestNeighbor(Terrain* west)
+{
+    if (west == west_)
+        return;
+
+    if (west_ && west_->GetNode())
+        UnsubscribeFromEvent(west_->GetNode(), E_TERRAINCREATED);
+
+    west_ = west;
+    if (west_ && west_->GetNode())
+    {
+        westID_ = west_->GetNode()->GetID();
+        SubscribeToEvent(west_->GetNode(), E_TERRAINCREATED, URHO3D_HANDLER(Terrain, HandleNeighborTerrainCreated));
+    }
+
+    UpdateEdgePatchNeighbors();
+    MarkNetworkUpdate();
+}
+
+void Terrain::SetEastNeighbor(Terrain* east)
+{
+    if (east == east_)
+        return;
+
+    if (east_ && east_->GetNode())
+        UnsubscribeFromEvent(east_->GetNode(), E_TERRAINCREATED);
+
+    east_ = east;
+    if (east_ && east_->GetNode())
+    {
+        eastID_ = east_->GetNode()->GetID();
+        SubscribeToEvent(east_->GetNode(), E_TERRAINCREATED, URHO3D_HANDLER(Terrain, HandleNeighborTerrainCreated));
+    }
+
+    UpdateEdgePatchNeighbors();
+    MarkNetworkUpdate();
+}
+
+void Terrain::SetNeighbors(Terrain* north, Terrain* south, Terrain* west, Terrain* east)
+{
+    if (north_ && north_->GetNode())
+        UnsubscribeFromEvent(north_->GetNode(), E_TERRAINCREATED);
+    if (south_ && south_->GetNode())
+        UnsubscribeFromEvent(south_->GetNode(), E_TERRAINCREATED);
+    if (west_ && west_->GetNode())
+        UnsubscribeFromEvent(west_->GetNode(), E_TERRAINCREATED);
+    if (east_ && east_->GetNode())
+        UnsubscribeFromEvent(east_->GetNode(), E_TERRAINCREATED);
+
+    north_ = north;
+    if (north_ && north_->GetNode())
+    {
+        northID_ = north_->GetNode()->GetID();
+        SubscribeToEvent(north_->GetNode(), E_TERRAINCREATED, URHO3D_HANDLER(Terrain, HandleNeighborTerrainCreated));
+    }
+    south_ = south;
+    if (south_ && south_->GetNode())
+    {
+        southID_ = south_->GetNode()->GetID();
+        SubscribeToEvent(south_->GetNode(), E_TERRAINCREATED, URHO3D_HANDLER(Terrain, HandleNeighborTerrainCreated));
+    }
+    west_ = west;
+    if (west_ && west_->GetNode())
+    {
+        westID_ = west_->GetNode()->GetID();
+        SubscribeToEvent(west_->GetNode(), E_TERRAINCREATED, URHO3D_HANDLER(Terrain, HandleNeighborTerrainCreated));
+    }
+    east_ = east;
+    if (east_ && east_->GetNode())
+    {
+        eastID_ = east_->GetNode()->GetID();
+        SubscribeToEvent(east_->GetNode(), E_TERRAINCREATED, URHO3D_HANDLER(Terrain, HandleNeighborTerrainCreated));
+    }
+
+    UpdateEdgePatchNeighbors();
+    MarkNetworkUpdate();
+}
+
 void Terrain::SetDrawDistance(float distance)
 void Terrain::SetDrawDistance(float distance)
 {
 {
     drawDistance_ = distance;
     drawDistance_ = distance;
@@ -409,6 +554,20 @@ TerrainPatch* Terrain::GetPatch(int x, int z) const
         return GetPatch((unsigned)(z * numPatches_.x_ + x));
         return GetPatch((unsigned)(z * numPatches_.x_ + x));
 }
 }
 
 
+TerrainPatch* Terrain::GetNeighborPatch(int x, int z) const
+{
+    if (z >= numPatches_.y_ && north_)
+        return north_->GetPatch(x, z - numPatches_.y_);
+    else if (z < 0 && south_)
+        return south_->GetPatch(x, z + south_->GetNumPatches().y_);
+    else if (x < 0 && west_)
+        return west_->GetPatch(x + west_->GetNumPatches().x_, z);
+    else if (x >= numPatches_.x_ && east_)
+        return east_->GetPatch(x - numPatches_.x_, z);
+    else 
+        return GetPatch(x, z);
+}
+
 float Terrain::GetHeight(const Vector3& worldPosition) const
 float Terrain::GetHeight(const Vector3& worldPosition) const
 {
 {
     if (node_)
     if (node_)
@@ -962,7 +1121,7 @@ void Terrain::CreateGeometry()
                 CalculateLodErrors(patch);
                 CalculateLodErrors(patch);
             }
             }
 
 
-            SetNeighbors(patch);
+            SetPatchNeighbors(patch);
         }
         }
     }
     }
 
 
@@ -1247,11 +1406,14 @@ void Terrain::CalculateLodErrors(TerrainPatch* patch)
     }
     }
 }
 }
 
 
-void Terrain::SetNeighbors(TerrainPatch* patch)
+void Terrain::SetPatchNeighbors(TerrainPatch* patch)
 {
 {
+    if (!patch)
+        return;
+
     const IntVector2& coords = patch->GetCoordinates();
     const IntVector2& coords = patch->GetCoordinates();
-    patch->SetNeighbors(GetPatch(coords.x_, coords.y_ + 1), GetPatch(coords.x_, coords.y_ - 1),
-        GetPatch(coords.x_ - 1, coords.y_), GetPatch(coords.x_ + 1, coords.y_));
+    patch->SetNeighbors(GetNeighborPatch(coords.x_, coords.y_ + 1), GetNeighborPatch(coords.x_, coords.y_ - 1),
+        GetNeighborPatch(coords.x_ - 1, coords.y_), GetNeighborPatch(coords.x_ + 1, coords.y_));
 }
 }
 
 
 bool Terrain::SetHeightMapInternal(Image* image, bool recreateNow)
 bool Terrain::SetHeightMapInternal(Image* image, bool recreateNow)
@@ -1283,4 +1445,28 @@ void Terrain::HandleHeightMapReloadFinished(StringHash eventType, VariantMap& ev
     CreateGeometry();
     CreateGeometry();
 }
 }
 
 
+void Terrain::HandleNeighborTerrainCreated(StringHash eventType, VariantMap& eventData)
+{
+    UpdateEdgePatchNeighbors();
+}
+
+void Terrain::UpdateEdgePatchNeighbors()
+{
+    for (int x = 1; x < numPatches_.x_ - 1; ++x)
+    {
+        SetPatchNeighbors(GetPatch(x, 0));
+        SetPatchNeighbors(GetPatch(x, numPatches_.y_ - 1));
+    }
+    for (int z = 1; z < numPatches_.y_ - 1; ++z)
+    {
+        SetPatchNeighbors(GetPatch(0, z));
+        SetPatchNeighbors(GetPatch(numPatches_.x_ - 1, z));
+    }
+
+    SetPatchNeighbors(GetPatch(0, 0));
+    SetPatchNeighbors(GetPatch(numPatches_.x_ - 1, 0));
+    SetPatchNeighbors(GetPatch(0, numPatches_.y_ - 1));
+    SetPatchNeighbors(GetPatch(numPatches_.x_ - 1, numPatches_.y_ - 1));
+}
+
 }
 }

+ 47 - 1
Source/Urho3D/Graphics/Terrain.h

@@ -67,6 +67,16 @@ public:
     bool SetHeightMap(Image* image);
     bool SetHeightMap(Image* image);
     /// Set material.
     /// Set material.
     void SetMaterial(Material* material);
     void SetMaterial(Material* material);
+    /// Set north (positive Z) neighbor terrain for seamless LOD changes across terrains.
+    void SetNorthNeighbor(Terrain* north);
+    /// Set south (negative Z) neighbor terrain for seamless LOD changes across terrains.
+    void SetSouthNeighbor(Terrain* south);
+    /// Set west (negative X) neighbor terrain for seamless LOD changes across terrains.
+    void SetWestNeighbor(Terrain* west);
+    /// Set east (positive X) neighbor terrain for seamless LOD changes across terrains.
+    void SetEastNeighbor(Terrain* east);
+    /// Set all neighbor terrains at once.
+    void SetNeighbors(Terrain* north, Terrain* south, Terrain* west, Terrain* east);
     /// Set draw distance for patches.
     /// Set draw distance for patches.
     void SetDrawDistance(float distance);
     void SetDrawDistance(float distance);
     /// Set shadow draw distance for patches.
     /// Set shadow draw distance for patches.
@@ -121,6 +131,8 @@ public:
     TerrainPatch* GetPatch(unsigned index) const;
     TerrainPatch* GetPatch(unsigned index) const;
     /// Return patch by patch coordinates.
     /// Return patch by patch coordinates.
     TerrainPatch* GetPatch(int x, int z) const;
     TerrainPatch* GetPatch(int x, int z) const;
+    /// Return patch by patch coordinates including neighbor terrains.
+    TerrainPatch* GetNeighborPatch(int x, int z) const;
     /// Return height at world coordinates.
     /// Return height at world coordinates.
     float GetHeight(const Vector3& worldPosition) const;
     float GetHeight(const Vector3& worldPosition) const;
     /// Return normal at world coordinates.
     /// Return normal at world coordinates.
@@ -128,6 +140,18 @@ public:
     /// Convert world position to heightmap pixel position. Note that the internal height data representation is reversed vertically, but in the heightmap image north is at the top.
     /// Convert world position to heightmap pixel position. Note that the internal height data representation is reversed vertically, but in the heightmap image north is at the top.
     IntVector2 WorldToHeightMap(const Vector3& worldPosition) const;
     IntVector2 WorldToHeightMap(const Vector3& worldPosition) const;
 
 
+    /// Return north neighbor terrain.
+    Terrain* GetNorthNeighbor() const { return north_; }
+    
+    /// Return south neighbor terrain.
+    Terrain* GetSouthNeighbor() const { return south_; }
+    
+    /// Return west neighbor terrain.
+    Terrain* GetWestNeighbor() const { return west_; }
+    
+    /// Return east neighbor terrain.
+    Terrain* GetEastNeighbor() const { return east_; }
+
     /// Return raw height data.
     /// Return raw height data.
     SharedArrayPtr<float> GetHeightData() const { return heightData_; }
     SharedArrayPtr<float> GetHeightData() const { return heightData_; }
 
 
@@ -202,11 +226,15 @@ private:
     /// Calculate LOD errors for a patch.
     /// Calculate LOD errors for a patch.
     void CalculateLodErrors(TerrainPatch* patch);
     void CalculateLodErrors(TerrainPatch* patch);
     /// Set neighbors for a patch.
     /// Set neighbors for a patch.
-    void SetNeighbors(TerrainPatch* patch);
+    void SetPatchNeighbors(TerrainPatch* patch);
     /// Set heightmap image and optionally recreate the geometry immediately. Return true if successful.
     /// Set heightmap image and optionally recreate the geometry immediately. Return true if successful.
     bool SetHeightMapInternal(Image* image, bool recreateNow);
     bool SetHeightMapInternal(Image* image, bool recreateNow);
     /// Handle heightmap image reload finished.
     /// Handle heightmap image reload finished.
     void HandleHeightMapReloadFinished(StringHash eventType, VariantMap& eventData);
     void HandleHeightMapReloadFinished(StringHash eventType, VariantMap& eventData);
+    /// Handle neighbor terrain geometry being created. Update the edge patch neighbors as necessary.
+    void HandleNeighborTerrainCreated(StringHash eventType, VariantMap& eventData);
+    /// Update edge patch neighbors when neighbor terrain(s) change or are recreated.
+    void UpdateEdgePatchNeighbors();
 
 
     /// Shared index buffer.
     /// Shared index buffer.
     SharedPtr<IndexBuffer> indexBuffer_;
     SharedPtr<IndexBuffer> indexBuffer_;
@@ -222,6 +250,14 @@ private:
     Vector<WeakPtr<TerrainPatch> > patches_;
     Vector<WeakPtr<TerrainPatch> > patches_;
     /// Draw ranges for different LODs and stitching combinations.
     /// Draw ranges for different LODs and stitching combinations.
     PODVector<Pair<unsigned, unsigned> > drawRanges_;
     PODVector<Pair<unsigned, unsigned> > drawRanges_;
+    /// North neighbor terrain.
+    WeakPtr<Terrain> north_;
+    /// South neighbor terrain.
+    WeakPtr<Terrain> south_;
+    /// West neighbor terrain.
+    WeakPtr<Terrain> west_;
+    /// East neighbor terrain.
+    WeakPtr<Terrain> east_;
     /// Vertex and height spacing.
     /// Vertex and height spacing.
     Vector3 spacing_;
     Vector3 spacing_;
     /// Vertex and height sacing at the time of last update.
     /// Vertex and height sacing at the time of last update.
@@ -272,8 +308,18 @@ private:
     float lodBias_;
     float lodBias_;
     /// Maximum lights.
     /// Maximum lights.
     unsigned maxLights_;
     unsigned maxLights_;
+    /// Node ID of north neighbor.
+    unsigned northID_;
+    /// Node ID of south neighbor.
+    unsigned southID_;
+    /// Node ID of west neighbor.
+    unsigned westID_;
+    /// Node ID of east neighbor.
+    unsigned eastID_;
     /// Terrain needs regeneration flag.
     /// Terrain needs regeneration flag.
     bool recreateTerrain_;
     bool recreateTerrain_;
+    /// Terrain neighbor attributes dirty flag.
+    bool neighborsDirty_;
 };
 };
 
 
 }
 }

+ 15 - 1
Source/Urho3D/LuaScript/pkgs/Graphics/Terrain.pkg

@@ -9,6 +9,11 @@ class Terrain : public Component
     void SetSmoothing(bool enable);
     void SetSmoothing(bool enable);
     bool SetHeightMap(Image* image);
     bool SetHeightMap(Image* image);
     void SetMaterial(Material* material);
     void SetMaterial(Material* material);
+    void SetNorthNeighbor(Terrain* north);
+    void SetSouthNeighbor(Terrain* south);
+    void SetWestNeighbor(Terrain* west);
+    void SetEastNeighbor(Terrain* east);
+    void SetNeighbors(Terrain* north, Terrain* south, Terrain* west, Terrain* east);
     void SetDrawDistance(float distance);
     void SetDrawDistance(float distance);
     void SetShadowDistance(float distance);
     void SetShadowDistance(float distance);
     void SetLodBias(float bias);
     void SetLodBias(float bias);
@@ -31,8 +36,13 @@ class Terrain : public Component
     bool GetSmoothing() const;
     bool GetSmoothing() const;
     Image* GetHeightMap() const;
     Image* GetHeightMap() const;
     Material* GetMaterial() const;
     Material* GetMaterial() const;
+    Terrain* GetNorthNeighbor() const;
+    Terrain* GetSouthNeighbor() const;
+    Terrain* GetWestNeighbor() const;
+    Terrain* GetEastNeighbor() const;
     TerrainPatch* GetPatch(unsigned index) const;
     TerrainPatch* GetPatch(unsigned index) const;
     TerrainPatch* GetPatch(int x, int z) const;
     TerrainPatch* GetPatch(int x, int z) const;
+    TerrainPatch* GetNeighborPatch(int x, int z) const;
     float GetHeight(const Vector3& worldPosition) const;
     float GetHeight(const Vector3& worldPosition) const;
     Vector3 GetNormal(const Vector3& worldPosition) const;
     Vector3 GetNormal(const Vector3& worldPosition) const;
     IntVector2 WorldToHeightMap(const Vector3& worldPosition) const;
     IntVector2 WorldToHeightMap(const Vector3& worldPosition) const;
@@ -49,7 +59,7 @@ class Terrain : public Component
     bool GetCastShadows() const;
     bool GetCastShadows() const;
     bool IsOccluder() const;
     bool IsOccluder() const;
     bool IsOccludee() const;
     bool IsOccludee() const;
-    
+
     tolua_property__get_set int patchSize;
     tolua_property__get_set int patchSize;
     tolua_property__get_set Vector3& spacing;
     tolua_property__get_set Vector3& spacing;
     tolua_readonly tolua_property__get_set IntVector2& numVertices;
     tolua_readonly tolua_property__get_set IntVector2& numVertices;
@@ -59,6 +69,10 @@ class Terrain : public Component
     tolua_property__get_set bool smoothing;
     tolua_property__get_set bool smoothing;
     tolua_property__get_set Image* heightMap;
     tolua_property__get_set Image* heightMap;
     tolua_property__get_set Material* material;
     tolua_property__get_set Material* material;
+    tolua_property__get_set Terrain* northNeighbor;
+    tolua_property__get_set Terrain* southNeighbor;
+    tolua_property__get_set Terrain* westNeighbor;
+    tolua_property__get_set Terrain* eastNeighbor;
     tolua_property__get_set float drawDistance;
     tolua_property__get_set float drawDistance;
     tolua_property__get_set float shadowDistance;
     tolua_property__get_set float shadowDistance;
     tolua_property__get_set float lodBias;
     tolua_property__get_set float lodBias;

+ 58 - 21
Source/Urho3D/Physics/PhysicsWorld.cpp

@@ -684,7 +684,7 @@ void PhysicsWorld::GetCollidingBodies(PODVector<RigidBody*>& result, const Rigid
 
 
     result.Clear();
     result.Clear();
 
 
-    for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
+    for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, ManifoldPair>::Iterator i = currentCollisions_.Begin();
          i != currentCollisions_.End(); ++i)
          i != currentCollisions_.End(); ++i)
     {
     {
         if (i->first_.first_ == body)
         if (i->first_.first_ == body)
@@ -887,18 +887,22 @@ void PhysicsWorld::SendCollisionEvents()
             WeakPtr<RigidBody> bodyWeakA(bodyA);
             WeakPtr<RigidBody> bodyWeakA(bodyA);
             WeakPtr<RigidBody> bodyWeakB(bodyB);
             WeakPtr<RigidBody> bodyWeakB(bodyB);
 
 
+            // First only store the collision pair as weak pointers and the manifold pointer, so user code can safely destroy
+            // objects during collision event handling
             Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> > bodyPair;
             Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> > bodyPair;
             if (bodyA < bodyB)
             if (bodyA < bodyB)
+            {
                 bodyPair = MakePair(bodyWeakA, bodyWeakB);
                 bodyPair = MakePair(bodyWeakA, bodyWeakB);
+                currentCollisions_[bodyPair].manifold_ = contactManifold;
+            }
             else
             else
+            {
                 bodyPair = MakePair(bodyWeakB, bodyWeakA);
                 bodyPair = MakePair(bodyWeakB, bodyWeakA);
-
-            // First only store the collision pair as weak pointers and the manifold pointer, so user code can safely destroy
-            // objects during collision event handling
-            currentCollisions_[bodyPair] = contactManifold;
+                currentCollisions_[bodyPair].flippedManifold_ = contactManifold;
+            }
         }
         }
 
 
-        for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
+        for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, ManifoldPair>::Iterator i = currentCollisions_.Begin();
              i != currentCollisions_.End(); ++i)
              i != currentCollisions_.End(); ++i)
         {
         {
             RigidBody* bodyA = i->first_.first_;
             RigidBody* bodyA = i->first_.first_;
@@ -906,8 +910,6 @@ void PhysicsWorld::SendCollisionEvents()
             if (!bodyA || !bodyB)
             if (!bodyA || !bodyB)
                 continue;
                 continue;
 
 
-            btPersistentManifold* contactManifold = i->second_;
-
             Node* nodeA = bodyA->GetNode();
             Node* nodeA = bodyA->GetNode();
             Node* nodeB = bodyB->GetNode();
             Node* nodeB = bodyB->GetNode();
             WeakPtr<Node> nodeWeakA(nodeA);
             WeakPtr<Node> nodeWeakA(nodeA);
@@ -924,13 +926,31 @@ void PhysicsWorld::SendCollisionEvents()
 
 
             contacts_.Clear();
             contacts_.Clear();
 
 
-            for (int j = 0; j < contactManifold->getNumContacts(); ++j)
+            // "Pointers not flipped"-manifold, send unmodified normals
+            btPersistentManifold* contactManifold = i->second_.manifold_;
+            if (contactManifold)
             {
             {
-                btManifoldPoint& point = contactManifold->getContactPoint(j);
-                contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
-                contacts_.WriteVector3(ToVector3(point.m_normalWorldOnB));
-                contacts_.WriteFloat(point.m_distance1);
-                contacts_.WriteFloat(point.m_appliedImpulse);
+                for (int j = 0; j < contactManifold->getNumContacts(); ++j)
+                {
+                    btManifoldPoint& point = contactManifold->getContactPoint(j);
+                    contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
+                    contacts_.WriteVector3(ToVector3(point.m_normalWorldOnB));
+                    contacts_.WriteFloat(point.m_distance1);
+                    contacts_.WriteFloat(point.m_appliedImpulse);
+                }
+            }
+            // "Pointers flipped"-manifold, flip normals also
+            contactManifold = i->second_.flippedManifold_;
+            if (contactManifold)
+            {
+                for (int j = 0; j < contactManifold->getNumContacts(); ++j)
+                {
+                    btManifoldPoint& point = contactManifold->getContactPoint(j);
+                    contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
+                    contacts_.WriteVector3(-ToVector3(point.m_normalWorldOnB));
+                    contacts_.WriteFloat(point.m_distance1);
+                    contacts_.WriteFloat(point.m_appliedImpulse);
+                }
             }
             }
 
 
             physicsCollisionData_[PhysicsCollision::P_CONTACTS] = contacts_.GetBuffer();
             physicsCollisionData_[PhysicsCollision::P_CONTACTS] = contacts_.GetBuffer();
@@ -966,14 +986,31 @@ void PhysicsWorld::SendCollisionEvents()
             if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
             if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
                 continue;
                 continue;
 
 
+            // Flip perspective to body B
             contacts_.Clear();
             contacts_.Clear();
-            for (int j = 0; j < contactManifold->getNumContacts(); ++j)
+            contactManifold = i->second_.manifold_;
+            if (contactManifold)
+            {
+                for (int j = 0; j < contactManifold->getNumContacts(); ++j)
+                {
+                    btManifoldPoint& point = contactManifold->getContactPoint(j);
+                    contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
+                    contacts_.WriteVector3(-ToVector3(point.m_normalWorldOnB));
+                    contacts_.WriteFloat(point.m_distance1);
+                    contacts_.WriteFloat(point.m_appliedImpulse);
+                }
+            }
+            contactManifold = i->second_.flippedManifold_;
+            if (contactManifold)
             {
             {
-                btManifoldPoint& point = contactManifold->getContactPoint(j);
-                contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
-                contacts_.WriteVector3(-ToVector3(point.m_normalWorldOnB));
-                contacts_.WriteFloat(point.m_distance1);
-                contacts_.WriteFloat(point.m_appliedImpulse);
+                for (int j = 0; j < contactManifold->getNumContacts(); ++j)
+                {
+                    btManifoldPoint& point = contactManifold->getContactPoint(j);
+                    contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
+                    contacts_.WriteVector3(ToVector3(point.m_normalWorldOnB));
+                    contacts_.WriteFloat(point.m_distance1);
+                    contacts_.WriteFloat(point.m_appliedImpulse);
+                }
             }
             }
 
 
             nodeCollisionData_[NodeCollision::P_BODY] = bodyB;
             nodeCollisionData_[NodeCollision::P_BODY] = bodyB;
@@ -996,7 +1033,7 @@ void PhysicsWorld::SendCollisionEvents()
     {
     {
         physicsCollisionData_[PhysicsCollisionEnd::P_WORLD] = this;
         physicsCollisionData_[PhysicsCollisionEnd::P_WORLD] = this;
 
 
-        for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator
+        for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, ManifoldPair>::Iterator
                  i = previousCollisions_.Begin(); i != previousCollisions_.End(); ++i)
                  i = previousCollisions_.Begin(); i != previousCollisions_.End(); ++i)
         {
         {
             if (!currentCollisions_.Contains(i->first_))
             if (!currentCollisions_.Contains(i->first_))

+ 18 - 2
Source/Urho3D/Physics/PhysicsWorld.h

@@ -96,6 +96,22 @@ struct DelayedWorldTransform
     Quaternion worldRotation_;
     Quaternion worldRotation_;
 };
 };
 
 
+/// Manifold pointers stored during collision processing.
+struct ManifoldPair
+{
+    /// Construct with defaults.
+    ManifoldPair() :
+        manifold_(0),
+        flippedManifold_(0)
+    {
+    }
+
+    /// Manifold without the body pointers flipped.
+    btPersistentManifold* manifold_;
+    /// Manifold with the body pointers flipped.
+    btPersistentManifold* flippedManifold_;
+};
+
 /// Custom overrides of physics internals. To use overrides, must be set before the physics component is created.
 /// Custom overrides of physics internals. To use overrides, must be set before the physics component is created.
 struct PhysicsWorldConfig
 struct PhysicsWorldConfig
 {
 {
@@ -301,9 +317,9 @@ private:
     /// Constraints in the world.
     /// Constraints in the world.
     PODVector<Constraint*> constraints_;
     PODVector<Constraint*> constraints_;
     /// Collision pairs on this frame.
     /// Collision pairs on this frame.
-    HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*> currentCollisions_;
+    HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, ManifoldPair> currentCollisions_;
     /// Collision pairs on the previous frame. Used to check if a collision is "new." Manifolds are not guaranteed to exist anymore.
     /// Collision pairs on the previous frame. Used to check if a collision is "new." Manifolds are not guaranteed to exist anymore.
-    HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*> previousCollisions_;
+    HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, ManifoldPair> previousCollisions_;
     /// Delayed (parented) world transform assignments.
     /// Delayed (parented) world transform assignments.
     HashMap<RigidBody*, DelayedWorldTransform> delayedWorldTransforms_;
     HashMap<RigidBody*, DelayedWorldTransform> delayedWorldTransforms_;
     /// Cache for trimesh geometry data by model and LOD level.
     /// Cache for trimesh geometry data by model and LOD level.

+ 1 - 1
Source/Urho3D/Scene/SplinePath.cpp

@@ -270,8 +270,8 @@ void SplinePath::SetControlPointIdsAttr(const VariantVector& value)
 void SplinePath::SetControlledIdAttr(unsigned value)
 void SplinePath::SetControlledIdAttr(unsigned value)
 {
 {
     if (value > 0 && value < M_MAX_UNSIGNED)
     if (value > 0 && value < M_MAX_UNSIGNED)
-
         controlledIdAttr_ = value;
         controlledIdAttr_ = value;
+
     dirty_ = true;
     dirty_ = true;
 }
 }
 
 

+ 2 - 2
bin/Data/LuaScripts/18_CharacterDemo.lua

@@ -355,9 +355,9 @@ function Character:HandleNodeCollision(eventType, eventData)
         local contactDistance = contacts:ReadFloat()
         local contactDistance = contacts:ReadFloat()
         local contactImpulse = contacts:ReadFloat()
         local contactImpulse = contacts:ReadFloat()
 
 
-        -- If contact is below node center and mostly vertical, assume it's a ground contact
+        -- If contact is below node center and pointing up, assume it's a ground contact
         if contactPosition.y < self.node.position.y + 1.0 then
         if contactPosition.y < self.node.position.y + 1.0 then
-            local level = Abs(contactNormal.y)
+            local level = contactNormal.y
             if level > 0.75 then
             if level > 0.75 then
                 self.onGround = true
                 self.onGround = true
             end
             end

+ 2 - 2
bin/Data/Scripts/18_CharacterDemo.as

@@ -378,10 +378,10 @@ class Character : ScriptObject
             float contactDistance = contacts.ReadFloat();
             float contactDistance = contacts.ReadFloat();
             float contactImpulse = contacts.ReadFloat();
             float contactImpulse = contacts.ReadFloat();
 
 
-            // If contact is below node center and mostly vertical, assume it's a ground contact
+            // If contact is below node center and pointing up, assume it's a ground contact
             if (contactPosition.y < (node.position.y + 1.0f))
             if (contactPosition.y < (node.position.y + 1.0f))
             {
             {
-                float level = Abs(contactNormal.y);
+                float level = contactNormal.y;
                 if (level > 0.75)
                 if (level > 0.75)
                     onGround = true;
                     onGround = true;
             }
             }

+ 2 - 2
bin/Data/Scripts/NinjaSnowWar/GameObject.as

@@ -110,10 +110,10 @@ class GameObject : ScriptObject
             float contactDistance = contacts.ReadFloat();
             float contactDistance = contacts.ReadFloat();
             float contactImpulse = contacts.ReadFloat();
             float contactImpulse = contacts.ReadFloat();
 
 
-            // If contact is below node center and mostly vertical, assume it's ground contact
+            // If contact is below node center and pointing up, assume it's ground contact
             if (contactPosition.y < node.position.y)
             if (contactPosition.y < node.position.y)
             {
             {
-                float level = Abs(contactNormal.y);
+                float level = contactNormal.y;
                 if (level > 0.75)
                 if (level > 0.75)
                     onGround = true;
                     onGround = true;
                 else
                 else

+ 2 - 0
bin/Data/UI/DefaultStyle.xml

@@ -108,11 +108,13 @@
         <element type="Text" internal="true">
         <element type="Text" internal="true">
             <attribute name="Color" value="0.9 1 0.9 1" />
             <attribute name="Color" value="0.9 1 0.9 1" />
             <attribute name="Selection Color" value="0.3 0.4 0.7 1" />
             <attribute name="Selection Color" value="0.3 0.4 0.7 1" />
+            <attribute name="Vert Alignment" value="Center" />
         </element>
         </element>
         <element type="BorderImage" internal="true">
         <element type="BorderImage" internal="true">
             <attribute name="Size" value="4 16" />
             <attribute name="Size" value="4 16" />
             <attribute name="Priority" value="1" />
             <attribute name="Priority" value="1" />
             <attribute name="Image Rect" value="12 0 16 16" />
             <attribute name="Image Rect" value="12 0 16 16" />
+            <attribute name="Vert Alignment" value="Center" />
         </element>
         </element>
     </element>
     </element>
     <element type="ListView" style="ScrollView">  <!-- Shortcut to copy all the styles from ScrollView -->
     <element type="ListView" style="ScrollView">  <!-- Shortcut to copy all the styles from ScrollView -->