Browse Source

Urho and Third Party updates (+14 squashed commits)
Squashed commits:
[0adf60b] Update Delta on Android C#
[f6d7cc2] Only build 64 bit iOS
[7e978c5] Adjustments to build setup of atomic projects to get rid of dependency errors on windows builds.
[2478856] Fix c++11 assert in EventHandler().
[54cd173] UI::GetWidgetAt() returns 0 if ui subsystem is not initialized.
[6876671] Fix sdl build error on some systems.
[0485d02] Urho3D -> Atomic strings
[0b29649] Pick right overload when using AnimatedModel.Model property
[2899b97] Update Android activity to SDL 2.0.5
[6dbf584] Updating CoreData
[f57e6a3] Default IK subsystem off, readd necessary C# enum values for Box2D
[172f5d9] Updates
[31aec9e] [2D] Fix issue when rigid body has been deleted during event, remove unnecessary C# enum massaging
[bc4f8be] Urho and Third Party updates (+5 squashed commits)
Squashed commits:
[9da0435] SDL 2.0.5 update, macOS building running
[5d87733] Fix directx build errors
[50a7a5a] Prevents newly subscribed handlers from executing on very this frame. Fixes E_KEYUP causing FeatureSamples to exit instead of returning to menu.
[5d60c4c] Script binding generation adjustments
[c449f18] Sync with upstream 129205a9b1fd94573c81feba76f897990990e8ec

Rokas Kupstys 8 years ago
parent
commit
404afffddf
100 changed files with 1829 additions and 492 deletions
  1. 21 1
      AUTHORS.md
  2. 2 2
      Build/CMake/Modules/AtomicIOS.cmake
  3. 1 1
      Build/CMake/Modules/AtomicMac.cmake
  4. 118 33
      Data/AtomicEditor/Deployment/Android/src/org/libsdl/app/SDLActivity.java
  5. 1 1
      Resources/CoreData/Materials/Particle.xml
  6. 4 1
      Resources/CoreData/RenderPaths/Deferred.xml
  7. 3 1
      Resources/CoreData/RenderPaths/DeferredHWDepth.xml
  8. 3 1
      Resources/CoreData/RenderPaths/ForwardDepth.xml
  9. 4 4
      Resources/CoreData/RenderPaths/ForwardHWDepth.xml
  10. 3 1
      Resources/CoreData/RenderPaths/PBRDeferred.xml
  11. 3 1
      Resources/CoreData/RenderPaths/PBRDeferredHWDepth.xml
  12. 4 1
      Resources/CoreData/RenderPaths/Prepass.xml
  13. 3 1
      Resources/CoreData/RenderPaths/PrepassHWDepth.xml
  14. 20 8
      Resources/CoreData/Shaders/GLSL/IBL.glsl
  15. 22 10
      Resources/CoreData/Shaders/GLSL/Lighting.glsl
  16. 35 0
      Resources/CoreData/Shaders/GLSL/LitParticle.glsl
  17. 3 3
      Resources/CoreData/Shaders/GLSL/LitSolid.glsl
  18. 96 5
      Resources/CoreData/Shaders/GLSL/PBR.glsl
  19. 11 3
      Resources/CoreData/Shaders/GLSL/PBRDeferred.glsl
  20. 14 5
      Resources/CoreData/Shaders/GLSL/PBRLitSolid.glsl
  21. 3 3
      Resources/CoreData/Shaders/GLSL/Shadow.glsl
  22. 82 24
      Resources/CoreData/Shaders/GLSL/Text.glsl
  23. 19 7
      Resources/CoreData/Shaders/GLSL/Transform.glsl
  24. 14 4
      Resources/CoreData/Shaders/GLSL/Uniforms.glsl
  25. 0 3
      Resources/CoreData/Shaders/GLSL/Unlit.glsl
  26. 90 0
      Resources/CoreData/Shaders/GLSL/UnlitParticle.glsl
  27. 7 7
      Resources/CoreData/Shaders/GLSL/Vegetation.glsl
  28. 1 1
      Resources/CoreData/Shaders/HLSL/Depth.hlsl
  29. 37 14
      Resources/CoreData/Shaders/HLSL/IBL.hlsl
  30. 24 10
      Resources/CoreData/Shaders/HLSL/Lighting.hlsl
  31. 49 0
      Resources/CoreData/Shaders/HLSL/LitParticle.hlsl
  32. 4 4
      Resources/CoreData/Shaders/HLSL/LitSolid.hlsl
  33. 101 8
      Resources/CoreData/Shaders/HLSL/PBR.hlsl
  34. 10 2
      Resources/CoreData/Shaders/HLSL/PBRDeferred.hlsl
  35. 15 8
      Resources/CoreData/Shaders/HLSL/PBRLitSolid.hlsl
  36. 9 6
      Resources/CoreData/Shaders/HLSL/Shadow.hlsl
  37. 3 0
      Resources/CoreData/Shaders/HLSL/Skybox.hlsl
  38. 59 21
      Resources/CoreData/Shaders/HLSL/Text.hlsl
  39. 9 2
      Resources/CoreData/Shaders/HLSL/Transform.hlsl
  40. 10 0
      Resources/CoreData/Shaders/HLSL/Uniforms.hlsl
  41. 145 0
      Resources/CoreData/Shaders/HLSL/UnlitParticle.hlsl
  42. 6 6
      Resources/CoreData/Shaders/HLSL/Vegetation.hlsl
  43. 5 0
      Resources/CoreData/Techniques/DiffLitParticleAlphaSoft.xml
  44. 5 0
      Resources/CoreData/Techniques/DiffLitParticleAlphaSoftExpand.xml
  45. 3 0
      Resources/CoreData/Techniques/DiffUnlitParticleAdd.xml
  46. 3 0
      Resources/CoreData/Techniques/DiffUnlitParticleAddSoft.xml
  47. 3 0
      Resources/CoreData/Techniques/DiffUnlitParticleAddSoftExpand.xml
  48. 3 0
      Resources/CoreData/Techniques/DiffUnlitParticleAlpha.xml
  49. 3 0
      Resources/CoreData/Techniques/DiffUnlitParticleAlphaSoft.xml
  50. 3 0
      Resources/CoreData/Techniques/DiffUnlitParticleAlphaSoftExpand.xml
  51. 15 0
      Script/AtomicNET/AtomicNET/Graphics/AnimatedModel.cs
  52. 1 1
      Script/AtomicNET/AtomicNET/IO/File.cs
  53. 14 0
      Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/AtomicActivity.java
  54. 298 95
      Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/SDLActivity.java
  55. 87 74
      Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/SDLSurface.java
  56. BIN
      Script/AtomicNET/Platform/Android/JavaSDLBin/atomicjavasdl-release.aar
  57. 3 1
      Script/Packages/Atomic/Core.json
  58. 1 1
      Script/Packages/Atomic/IO.json
  59. 1 1
      Script/Packages/Atomic/Scene.json
  60. 3 8
      Source/Atomic/Atomic2D/AnimatedSprite2D.cpp
  61. 2 2
      Source/Atomic/Atomic2D/AnimatedSprite2D.h
  62. 7 12
      Source/Atomic/Atomic2D/AnimationSet2D.cpp
  63. 3 3
      Source/Atomic/Atomic2D/AnimationSet2D.h
  64. 1 1
      Source/Atomic/Atomic2D/Atomic2D.cpp
  65. 1 1
      Source/Atomic/Atomic2D/Atomic2D.h
  66. 44 0
      Source/Atomic/Atomic2D/Atomic2DEvents.h
  67. 1 1
      Source/Atomic/Atomic2D/CollisionBox2D.cpp
  68. 1 1
      Source/Atomic/Atomic2D/CollisionBox2D.h
  69. 1 1
      Source/Atomic/Atomic2D/CollisionChain2D.cpp
  70. 1 1
      Source/Atomic/Atomic2D/CollisionChain2D.h
  71. 1 1
      Source/Atomic/Atomic2D/CollisionCircle2D.cpp
  72. 1 1
      Source/Atomic/Atomic2D/CollisionCircle2D.h
  73. 1 1
      Source/Atomic/Atomic2D/CollisionEdge2D.cpp
  74. 1 1
      Source/Atomic/Atomic2D/CollisionEdge2D.h
  75. 1 1
      Source/Atomic/Atomic2D/CollisionPolygon2D.cpp
  76. 1 1
      Source/Atomic/Atomic2D/CollisionPolygon2D.h
  77. 9 1
      Source/Atomic/Atomic2D/CollisionShape2D.cpp
  78. 1 1
      Source/Atomic/Atomic2D/CollisionShape2D.h
  79. 36 3
      Source/Atomic/Atomic2D/Constraint2D.cpp
  80. 10 2
      Source/Atomic/Atomic2D/Constraint2D.h
  81. 27 3
      Source/Atomic/Atomic2D/ConstraintDistance2D.cpp
  82. 6 1
      Source/Atomic/Atomic2D/ConstraintDistance2D.h
  83. 11 3
      Source/Atomic/Atomic2D/ConstraintFriction2D.cpp
  84. 1 1
      Source/Atomic/Atomic2D/ConstraintFriction2D.h
  85. 6 2
      Source/Atomic/Atomic2D/ConstraintGear2D.cpp
  86. 1 1
      Source/Atomic/Atomic2D/ConstraintGear2D.h
  87. 26 6
      Source/Atomic/Atomic2D/ConstraintMotor2D.cpp
  88. 1 1
      Source/Atomic/Atomic2D/ConstraintMotor2D.h
  89. 21 16
      Source/Atomic/Atomic2D/ConstraintMouse2D.cpp
  90. 1 3
      Source/Atomic/Atomic2D/ConstraintMouse2D.h
  91. 31 7
      Source/Atomic/Atomic2D/ConstraintPrismatic2D.cpp
  92. 1 1
      Source/Atomic/Atomic2D/ConstraintPrismatic2D.h
  93. 1 1
      Source/Atomic/Atomic2D/ConstraintPulley2D.cpp
  94. 1 1
      Source/Atomic/Atomic2D/ConstraintPulley2D.h
  95. 31 7
      Source/Atomic/Atomic2D/ConstraintRevolute2D.cpp
  96. 1 1
      Source/Atomic/Atomic2D/ConstraintRevolute2D.h
  97. 6 2
      Source/Atomic/Atomic2D/ConstraintRope2D.cpp
  98. 1 1
      Source/Atomic/Atomic2D/ConstraintRope2D.h
  99. 11 3
      Source/Atomic/Atomic2D/ConstraintWeld2D.cpp
  100. 1 1
      Source/Atomic/Atomic2D/ConstraintWeld2D.h

+ 21 - 1
AUTHORS.md

@@ -72,27 +72,35 @@ Licensed under the MIT license, see [License.txt](https://github.com/urho3d/Urho
 
 ##Credits
 Urho3D development, contributions and bugfixes by:
-- Lasse Öörni ([email protected], AgentC at GameDev.net)
+- Lasse Öörni
 - Wei Tjong Yao
 - Aster Jian
 - Vivienne Anthony
 - Colin Barrett
 - Erik Beran
+- Gauthier Billot
 - Loic Blot
 - Danny Boisvert
 - Sergey Bosko
+- Lisandro Bruzzo
 - Carlo Carollo
 - Pete Chown
 - Christian Clavet
 - Sebastian Delatorre (primitivewaste)
 - Josh Engebretson
+- Simon Flores
+- Manuel Freiberger
 - Chris Friesen
 - Alex Fuller
 - Henrik Heino
 - Mika Heinonen
+- Victor Holt
+- Johnathan Jenkins
 - Jukka Jylänki
 - Graham King
 - Jason Kinzer
+- Cameron Kline
+- Jan Korous
 - Eugene Kozlov
 - Gunnar Kriik
 - Aliaksandr Kryvashein
@@ -100,11 +108,13 @@ Urho3D development, contributions and bugfixes by:
 - Rokas Kupstys
 - Ali Kämäräinen
 - Pete Leigh
+- Arnis Lielturks
 - Frode 'Modanung' Lindeijer
 - Thorbjørn Lindeijer
 - Nathanial Lydick
 - Xavier Maupeu
 - Jonne Nauha
+- Huy Nguyen
 - Paul Noome
 - David Palacios
 - Alex Parlett
@@ -113,27 +123,35 @@ Urho3D development, contributions and bugfixes by:
 - Vladimir Pobedinsky
 - Franck Poulain
 - Pranjal Raihan
+- Mariusz Richtscheid
 - Nick Royer
+- Jonathan Sandusky
 - Miika Santala
 - Anatoly Sennov
+- Matan Shukry
 - Bengt Soderstrom
 - Hualin Song
 - James Thomas
 - Joshua Tippetts
+- Konstantin Tomashevich
 - Yusuf Umar
+- Mateus Vendramini
 - Daniel Wiberg
 - Steven Zhang
 - AGreatFish
 - BlueMagnificent
 - Enhex
+- Fastran
 - Firegorilla
 - Gordon-F
 - Lumak
 - Magic.Lixin
 - Mike3D
 - MonkeyFirst
+- Ner'zhul
 - Newb I the Newbd
 - OvermindDL1
+- PredatorMF
 - Scellow
 - Skrylar
 - TheComet93
@@ -149,6 +167,7 @@ Urho3D development, contributions and bugfixes by:
 - dragonCASTjosh
 - feltech
 - fredakilla
+- gleblebedev
 - hdunderscore
 - lvshiling
 - marynate
@@ -156,6 +175,7 @@ Urho3D development, contributions and bugfixes by:
 - neat3d
 - nemerle
 - ninjastone
+- orefkov
 - proller
 - raould
 - rasteron

+ 2 - 2
Build/CMake/Modules/AtomicIOS.cmake

@@ -10,7 +10,7 @@ set(CMAKE_OSX_SYSROOT iphoneos)    # Set Base SDK to "Latest iOS"
 set(CMAKE_OSX_DEPLOYMENT_TARGET "")
 set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
 
-set(CMAKE_OSX_ARCHITECTURES $(ARCHS_STANDARD_INCLUDING_64_BIT))
+set(CMAKE_OSX_ARCHITECTURES arm64)
 
 # This is a CMake hack in order to make standard CMake check modules that use try_compile() internally work on iOS platform
 # The injected "flags" are not compiler flags, they are actually CMake variables meant for another CMake subprocess that builds the source file being passed in the try_compile() command
@@ -24,4 +24,4 @@ message(${IOS_SYSROOT})
 
 set(CMAKE_FIND_ROOT_PATH ${IOS_SYSROOT})
 
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AudioToolbox -framework CoreAudio -framework CoreGraphics -framework Foundation -framework OpenGLES -framework QuartzCore -framework UIKit -framework CoreMotion -framework GameController")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AudioToolbox -framework AVFoundation -framework CoreAudio -framework CoreGraphics -framework Foundation -framework OpenGLES -framework QuartzCore -framework UIKit -framework CoreMotion -framework GameController")

+ 1 - 1
Build/CMake/Modules/AtomicMac.cmake

@@ -20,4 +20,4 @@ else ()
 endif ()
 
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -std=gnu++11 -stdlib=libc++")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -framework AudioUnit -framework Carbon -framework Cocoa -framework CoreAudio -framework CoreVideo -framework ForceFeedback -framework IOKit -framework OpenGL -framework CoreServices -framework Security")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -framework AudioToolbox -framework Carbon -framework Cocoa -framework CoreAudio -framework CoreVideo -framework ForceFeedback -framework IOKit -framework OpenGL -framework CoreServices -framework Security")

+ 118 - 33
Data/AtomicEditor/Deployment/Android/src/org/libsdl/app/SDLActivity.java

@@ -21,7 +21,7 @@ import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
-import android.widget.AbsoluteLayout;
+import android.widget.RelativeLayout;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -60,6 +60,7 @@ public class SDLActivity extends Activity {
 
     // Audio
     protected static AudioTrack mAudioTrack;
+    protected static AudioRecord mAudioRecord;
 
     // Urho3D: flag to load the .so and a new method load them
     private static boolean mIsSharedLibraryLoaded = false;
@@ -92,6 +93,7 @@ public class SDLActivity extends Activity {
         mJoystickHandler = null;
         mSDLThread = null;
         mAudioTrack = null;
+        mAudioRecord = null;
         mExitCalledFromJava = false;
         mIsPaused = false;
         mIsSurfaceReady = false;
@@ -182,7 +184,7 @@ public class SDLActivity extends Activity {
             mJoystickHandler = new SDLJoystickHandler();
         }
 
-        mLayout = new AbsoluteLayout(this);
+        mLayout = new RelativeLayout(this);
         mLayout.addView(mSurface);
 
         setContentView(mLayout);
@@ -380,7 +382,10 @@ public class SDLActivity extends Activity {
                 break;
             case COMMAND_TEXTEDIT_HIDE:
                 if (mTextEdit != null) {
-                    mTextEdit.setVisibility(View.GONE);
+                    // Note: On some devices setting view to GONE creates a flicker in landscape.
+                    // Setting the View's sizes to 0 is similar to GONE but without the flicker.
+                    // The sizes will be set to useful values when the keyboard is shown again.
+                    mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
 
                     InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
                     imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
@@ -518,8 +523,9 @@ public class SDLActivity extends Activity {
 
         @Override
         public void run() {
-            AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(
-                    w, h + HEIGHT_PADDING, x, y);
+            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
+            params.leftMargin = x;
+            params.topMargin = y;
 
             if (mTextEdit == null) {
                 mTextEdit = new DummyEdit(getContext());
@@ -557,7 +563,7 @@ public class SDLActivity extends Activity {
     /**
      * This method is called by SDL using JNI.
      */
-    public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+    public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
         int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
         int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
         int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
@@ -636,13 +642,72 @@ public class SDLActivity extends Activity {
     /**
      * This method is called by SDL using JNI.
      */
-    public static void audioQuit() {
+    public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
+        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
+        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+
+        Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+        // Let the user pick a larger buffer if they really want -- but ye
+        // gods they probably shouldn't, the minimums are horrifyingly high
+        // latency already
+        desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
+
+        if (mAudioRecord == null) {
+            mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
+                    channelConfig, audioFormat, desiredFrames * frameSize);
+
+            // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
+            if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+                Log.e(TAG, "Failed during initialization of AudioRecord");
+                mAudioRecord.release();
+                mAudioRecord = null;
+                return -1;
+            }
+
+            mAudioRecord.startRecording();
+        }
+
+        Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+        return 0;
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
+        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
+        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+        return mAudioRecord.read(buffer, 0, buffer.length);
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
+        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
+        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+        return mAudioRecord.read(buffer, 0, buffer.length);
+    }
+
+
+    /** This method is called by SDL using JNI. */
+    public static void audioClose() {
         if (mAudioTrack != null) {
             mAudioTrack.stop();
+            mAudioTrack.release();
             mAudioTrack = null;
         }
     }
 
+    /** This method is called by SDL using JNI. */
+    public static void captureClose() {
+        if (mAudioRecord != null) {
+            mAudioRecord.stop();
+            mAudioRecord.release();
+            mAudioRecord = null;
+        }
+    }
+
+
     // Input
 
     /**
@@ -677,6 +742,21 @@ public class SDLActivity extends Activity {
         }
     }
 
+    // Check if a given device is considered a possible SDL joystick
+    public static boolean isDeviceSDLJoystick(int deviceId) {
+        InputDevice device = InputDevice.getDevice(deviceId);
+        // We cannot use InputDevice.isVirtual before API 16, so let's accept
+        // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
+        if ((device == null) || (deviceId < 0)) {
+            return false;
+        }
+        int sources = device.getSources();
+        return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
+                ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
+                ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
+        );
+    }
+
     // APK expansion files support
 
     /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
@@ -685,15 +765,6 @@ public class SDLActivity extends Activity {
     /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
     private Method expansionFileMethod;
 
-    /**
-     * This method was called by SDL using JNI.
-     * @deprecated because of an incorrect name
-     */
-    @Deprecated
-    public InputStream openAPKExtensionInputStream(String fileName) throws IOException {
-        return openAPKExpansionInputStream(fileName);
-    }
-
     /**
      * This method is called by SDL using JNI.
      * @return an InputStream on success or null if no expansion file was used.
@@ -1183,11 +1254,14 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
     @Override
     public boolean onKey(View  v, int keyCode, KeyEvent event) {
         // Dispatch the different events depending on where they come from
-        // Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
-        // So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as KEYBOARD
-
-        if ( (event.getSource() & InputDevice.SOURCE_GAMEPAD) != 0 ||
-                   (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) {
+        // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
+        // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
+        //
+        // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
+        // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
+        // So, retrieve the device itself and check all of its sources
+        if (SDLActivity.isDeviceSDLJoystick(event.getDeviceId())) {
+            // Note that we process events with specific key codes here
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
                     return true;
@@ -1199,7 +1273,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
             }
         }
 
-        if( (event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
+        if ((event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 //Log.v("SDL", "key down: " + keyCode);
                 SDLActivity.onNativeKeyDown(keyCode);
@@ -1212,6 +1286,20 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
             }
         }
 
+        if ((event.getSource() & InputDevice.SOURCE_MOUSE) != 0) {
+            // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
+            // they are ignored here because sending them as mouse input to SDL is messy
+            if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
+                switch (event.getAction()) {
+                case KeyEvent.ACTION_DOWN:
+                case KeyEvent.ACTION_UP:
+                    // mark the event as handled or it will be handled by system
+                    // handling KEYCODE_BACK by system will call onBackPressed()
+                    return true;
+                }
+            }
+        }
+
         return false;
     }
 
@@ -1230,7 +1318,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
         // !!! FIXME: dump this SDK check after 2.0.4 ships and require API14.
         if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) {
             if (Build.VERSION.SDK_INT < 14) {
-                mouseButton = 1;    // For Android==12 all mouse buttons are the left button
+                mouseButton = 1; // all mouse buttons are the left button
             } else {
                 try {
                     mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
@@ -1345,7 +1433,7 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
             }
             SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
                                       y / SensorManager.GRAVITY_EARTH,
-                                      event.values[2] / SensorManager.GRAVITY_EARTH - 1);
+                                      event.values[2] / SensorManager.GRAVITY_EARTH);
         }
     }
 }
@@ -1389,7 +1477,7 @@ class DummyEdit extends View implements View.OnKeyListener {
         // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
         // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
         // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
-        // FIXME: A more effective solution would be to change our Layout from AbsoluteLayout to Relative or Linear
+        // FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
         // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
         // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
         if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
@@ -1428,7 +1516,7 @@ class SDLInputConnection extends BaseInputConnection {
          */
         int keyCode = event.getKeyCode();
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            if (event.isPrintingKey()) {
+            if (event.isPrintingKey() || keyCode == KeyEvent.KEYCODE_SPACE) {
                 commitText(String.valueOf((char) event.getUnicodeChar()), 1);
             }
             SDLActivity.onNativeKeyDown(keyCode);
@@ -1529,9 +1617,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
             if (joystick == null) {
                 joystick = new SDLJoystick();
                 InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
-
-                // Urho3D - revert back commit 34a0b0478654e8dfaf111aecc0e4535875c4ec87 as it does more harm than good
-                if( (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+                if (SDLActivity.isDeviceSDLJoystick(deviceIds[i])) {
                     joystick.device_id = deviceIds[i];
                     joystick.name = joystickDevice.getName();
                     joystick.axes = new ArrayList<InputDevice.MotionRange>();
@@ -1540,7 +1626,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
                     List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
                     Collections.sort(ranges, new RangeComparator());
                     for (InputDevice.MotionRange range : ranges ) {
-                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) {
+                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
                             if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
                                 range.getAxis() == MotionEvent.AXIS_HAT_Y) {
                                 joystick.hats.add(range);
@@ -1594,7 +1680,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
 
     @Override
     public boolean handleMotionEvent(MotionEvent event) {
-        if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
+        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
             int actionPointerIndex = event.getActionIndex();
             int action = event.getActionMasked();
             switch(action) {
@@ -1633,8 +1719,7 @@ class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
             case InputDevice.SOURCE_JOYSTICK:
             case InputDevice.SOURCE_GAMEPAD:
             case InputDevice.SOURCE_DPAD:
-                SDLActivity.handleJoystickMotionEvent(event);
-                return true;
+                return SDLActivity.handleJoystickMotionEvent(event);
 
             case InputDevice.SOURCE_MOUSE:
                 action = event.getActionMasked();

+ 1 - 1
Resources/CoreData/Materials/Particle.xml

@@ -1,4 +1,4 @@
 <material>
-    <technique name="Techniques/DiffVColAdd.xml" />
+    <technique name="Techniques/DiffUnlitParticleAdd.xml" />
     <texture unit="diffuse" name="Textures/Flare.dds" />
 </material>

+ 4 - 1
Resources/CoreData/RenderPaths/Deferred.xml

@@ -2,6 +2,7 @@
     <rendertarget name="albedo" sizedivisor="1 1" format="rgba" />
     <rendertarget name="normal" sizedivisor="1 1" format="rgba" />
     <rendertarget name="depth" sizedivisor="1 1" format="lineardepth" />
+    <command type="clear" color="1 1 1 1" output="depth" />
     <command type="clear" color="fog" depth="1.0" stencil="0" />
     <command type="scenepass" pass="deferred" marktostencil="true" vertexlights="true" metadata="gbuffer">
         <output index="0" name="viewport" />
@@ -18,6 +19,8 @@
     <command type="scenepass" pass="refract">
         <texture unit="environment" name="viewport" />
     </command>
-    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" />
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha">
+        <texture unit="depth" name="depth" />
+    </command>
     <command type="scenepass" pass="postalpha" sort="backtofront" />
 </renderpath>

+ 3 - 1
Resources/CoreData/RenderPaths/DeferredHWDepth.xml

@@ -18,6 +18,8 @@
     <command type="scenepass" pass="refract" depthstencil="depth">
         <texture unit="environment" name="viewport" />
     </command>
-    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" />
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" psdefines="HWDEPTH">
+        <texture unit="depth" name="depth" />
+    </command>
     <command type="scenepass" pass="postalpha" sort="backtofront" depthstencil="depth" />
 </renderpath>

+ 3 - 1
Resources/CoreData/RenderPaths/ForwardDepth.xml

@@ -9,6 +9,8 @@
     <command type="scenepass" pass="refract">
         <texture unit="environment" name="viewport" />
     </command>
-    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" />
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha">
+        <texture unit="depth" name="depth" />
+    </command>
     <command type="scenepass" pass="postalpha" sort="backtofront" />
 </renderpath>

+ 4 - 4
Resources/CoreData/RenderPaths/ForwardHWDepth.xml

@@ -1,14 +1,14 @@
 <renderpath>
     <rendertarget name="depth" sizedivisor="1 1" format="readabledepth" />
-    <command type="clear" depth="1.0" output="depth" />
-    <command type="scenepass" pass="shadow" output="depth" />
-    <command type="clear" color="fog" depthstencil="depth" />
+    <command type="clear" color="fog" depth="1.0" stencil="0" depthstencil="depth" />
     <command type="scenepass" pass="base" vertexlights="true" metadata="base" depthstencil="depth" />
     <command type="forwardlights" pass="light" depthstencil="depth" />
     <command type="scenepass" pass="postopaque" depthstencil="depth" />
     <command type="scenepass" pass="refract" depthstencil="depth">
         <texture unit="environment" name="viewport" />
     </command>
-    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth"  />
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" psdefines="HWDEPTH">
+        <texture unit="depth" name="depth" />
+    </command>
     <command type="scenepass" pass="postalpha" sort="backtofront" depthstencil="depth" />
 </renderpath>

+ 3 - 1
Resources/CoreData/RenderPaths/PBRDeferred.xml

@@ -25,6 +25,8 @@
     <command type="scenepass" pass="refract">
         <texture unit="environment" name="viewport" />
     </command>
-    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" />
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha">
+        <texture unit="depth" name="depth" />
+    </command>
     <command type="scenepass" pass="postalpha" sort="backtofront" />
 </renderpath>

+ 3 - 1
Resources/CoreData/RenderPaths/PBRDeferredHWDepth.xml

@@ -24,6 +24,8 @@
     <command type="scenepass" pass="refract" depthstencil="depth">
         <texture unit="environment" name="viewport" />
     </command>
-    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" />
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" psdefines="HWDEPTH">
+        <texture unit="depth" name="depth" />
+    </command>
     <command type="scenepass" pass="postalpha" sort="backtofront" depthstencil="depth" />
 </renderpath>

+ 4 - 1
Resources/CoreData/RenderPaths/Prepass.xml

@@ -2,6 +2,7 @@
     <rendertarget name="light" sizedivisor="1 1"  format="rgba" />
     <rendertarget name="normal" sizedivisor="1 1" format="rgba" />
     <rendertarget name="depth" sizedivisor="1 1"  format="lineardepth" />
+    <command type="clear" color="1 1 1 1" output="depth" />
     <command type="clear" color="fog" depth="1.0" stencil="0" />
     <command type="scenepass" pass="prepass" marktostencil="true" metadata="gbuffer">
         <output index="0" name="normal" />
@@ -19,6 +20,8 @@
     <command type="scenepass" pass="refract">
         <texture unit="environment" name="viewport" />
     </command>
-    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" />
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha">
+        <texture unit="depth" name="depth" />
+    </command>
     <command type="scenepass" pass="postalpha" sort="backtofront" />
 </renderpath>

+ 3 - 1
Resources/CoreData/RenderPaths/PrepassHWDepth.xml

@@ -16,6 +16,8 @@
     <command type="scenepass" pass="refract" depthstencil="depth">
         <texture unit="environment" name="viewport" />
     </command>
-    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" />
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" psdefines="HWDEPTH">
+        <texture unit="depth" name="depth" />
+    </command>
     <command type="scenepass" pass="postalpha" sort="backtofront" depthstencil="depth" />
 </renderpath>

+ 20 - 8
Resources/CoreData/Shaders/GLSL/IBL.glsl

@@ -236,10 +236,10 @@
         return mix(normal, reflection, lerpFactor);
     }
 
-    float GetMipFromRougness(float roughness)
+    float GetMipFromRoughness(float roughness)
     {
-        float smoothness = 1.0 - roughness;
-        return (1.0 - smoothness * smoothness) * 10.0;
+        float Level = 3 - 1.15 * log2( roughness );
+        return 9.0 - 1 - Level;
     }
 
 
@@ -253,6 +253,18 @@
         return SpecularColor * AB.x + AB.y;
     }
 
+    vec3 FixCubeLookup(vec3 v) 
+    {
+        float M = max(max(abs(v.x), abs(v.y)), abs(v.z));
+        float scale = (1024 - 1) / 1024;
+
+        if (abs(v.x) != M) v.x += scale;
+        if (abs(v.y) != M) v.y += scale;
+        if (abs(v.z) != M) v.z += scale; 
+
+        return v;
+    }
+
     /// Calculate IBL contributation
     ///     reflectVec: reflection vector for cube sampling
     ///     wsNormal: surface normal in word space
@@ -267,17 +279,17 @@
         // PMREM Mipmapmode https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/
         //float GlossScale = 16.0;
         //float GlossBias = 5.0;
-        float mipSelect = roughness * 9.0; //exp2(GlossScale * roughness * roughness + GlossBias) - exp2(GlossBias);
+        float mipSelect = GetMipFromRoughness(roughness); //exp2(GlossScale * roughness * roughness + GlossBias) - exp2(GlossBias);
 
         // OpenGL ES does not support textureLod without extensions and does not have the sZoneCubeMap sampler,
         // so for now, sample without explicit LOD, and from the environment sampler, where the zone texture will be put
         // on mobile hardware
         #ifndef GL_ES
-            vec3 cube = textureLod(sZoneCubeMap, reflectVec, mipSelect).rgb;
-            vec3 cubeD = textureLod(sZoneCubeMap, wsNormal, 9.0).rgb;
+            vec3 cube = textureLod(sZoneCubeMap, FixCubeLookup(reflectVec), mipSelect).rgb;
+            vec3 cubeD = textureLod(sZoneCubeMap, FixCubeLookup(wsNormal), 9.0).rgb;
         #else
-            vec3 cube = textureCube(sEnvCubeMap, reflectVec).rgb;
-            vec3 cubeD = textureCube(sEnvCubeMap, wsNormal).rgb;
+            vec3 cube = textureCube(sEnvCubeMap, FixCubeLookup(reflectVec)).rgb;
+            vec3 cubeD = textureCube(sEnvCubeMap, FixCubeLookup(wsNormal)).rgb;
         #endif
 
         // Fake the HDR texture

+ 22 - 10
Resources/CoreData/Shaders/GLSL/Lighting.glsl

@@ -127,17 +127,29 @@ float GetDiffuse(vec3 normal, vec3 worldPos, out vec3 lightDir)
 
 float GetAtten(vec3 normal, vec3 worldPos, out vec3 lightDir)
 {
-     #ifdef DIRLIGHT
-        lightDir = cLightDirPS;
-        return clamp(dot(normal, lightDir), 0.0, 1.0);
-    #else
-        vec3 lightVec = (cLightPosPS.xyz - worldPos) * cLightPosPS.w;
-        float lightDist = length(lightVec);
-        float falloff = pow(clamp(1.0 - pow(lightDist / 1.0, 4.0), 0.0, 1.0), 2.0) / (pow(lightDist, 2.0) + 1.0);
+    lightDir = cLightDirPS;
+    return clamp(dot(normal, lightDir), 0.0, 1.0);
+}
+
+float GetAttenPoint(vec3 normal, vec3 worldPos, out vec3 lightDir)
+{
+    vec3 lightVec = (cLightPosPS.xyz - worldPos) * cLightPosPS.w;
+    float lightDist = length(lightVec);
+    float falloff = pow(clamp(1.0 - pow(lightDist / 1.0, 4.0), 0.0, 1.0), 2.0) * 3.14159265358979323846 / (4.0 * 3.14159265358979323846)*(pow(lightDist, 2.0) + 1.0);
+    lightDir = lightVec / lightDist;
+    return clamp(dot(normal, lightDir), 0.0, 1.0) * falloff;
+
+}
+
+float GetAttenSpot(vec3 normal, vec3 worldPos, out vec3 lightDir)
+{
+    vec3 lightVec = (cLightPosPS.xyz - worldPos) * cLightPosPS.w;
+    float lightDist = length(lightVec);
+    float falloff = pow(clamp(1.0 - pow(lightDist / 1.0, 4.0), 0.0, 1.0), 2.0) / (pow(lightDist, 2.0) + 1.0);
+
+    lightDir = lightVec / lightDist;
+    return clamp(dot(normal, lightDir), 0.0, 1.0) * falloff;
 
-        lightDir = lightVec / vec3(lightDist, lightDist, lightDist);
-        return clamp(dot(normal, lightDir), 0.0, 1.0) * falloff;
-    #endif
 }
 
 float GetDiffuseVolumetric(vec3 worldPos)

+ 35 - 0
Resources/CoreData/Shaders/GLSL/LitParticle.glsl

@@ -1,6 +1,7 @@
 #include "Uniforms.glsl"
 #include "Samplers.glsl"
 #include "Transform.glsl"
+#include "ScreenPos.glsl"
 #include "Lighting.glsl"
 #include "Fog.glsl"
 
@@ -9,6 +10,10 @@ varying vec4 vWorldPos;
 #ifdef VERTEXCOLOR
     varying vec4 vColor;
 #endif
+#ifdef SOFTPARTICLES
+    varying vec4 vScreenPos;
+    uniform float cSoftParticleFadeScale;
+#endif
 #ifdef PERPIXEL
     #ifdef SHADOW
         #ifndef GL_ES
@@ -35,6 +40,10 @@ void VS()
     vTexCoord = GetTexCoord(iTexCoord);
     vWorldPos = vec4(worldPos, GetDepth(gl_Position));
 
+    #ifdef SOFTPARTICLES
+        vScreenPos = GetScreenPos(gl_Position);
+    #endif
+
     #ifdef VERTEXCOLOR
         vColor = iColor;
     #endif
@@ -93,6 +102,32 @@ void PS()
         float fogFactor = GetFogFactor(vWorldPos.w);
     #endif
 
+    // Soft particle fade
+    // In expand mode depth test should be off. In that case do manual alpha discard test first to reduce fill rate
+    #ifdef SOFTPARTICLES
+        #ifdef EXPAND
+            if (diffColor.a < 0.01)
+                discard;
+        #endif
+
+        float particleDepth = vWorldPos.w;
+        #ifdef HWDEPTH
+            float depth = ReconstructDepth(texture2DProj(sDepthBuffer, vScreenPos).r);
+        #else
+            float depth = DecodeDepth(texture2DProj(sDepthBuffer, vScreenPos).rgb);
+        #endif
+
+        #ifdef EXPAND
+            float diffZ = max(particleDepth - depth, 0.0) * (cFarClipPS - cNearClipPS);
+            float fade = clamp(diffZ * cSoftParticleFadeScale, 0.0, 1.0);
+        #else
+            float diffZ = (depth - particleDepth) * (cFarClipPS - cNearClipPS);
+            float fade = clamp(1.0 - diffZ * cSoftParticleFadeScale, 0.0, 1.0);
+        #endif
+
+        diffColor.a = max(diffColor.a - fade, 0.0);
+    #endif
+
     #ifdef PERPIXEL
         // Per-pixel forward lighting
         vec3 lightColor;

+ 3 - 3
Resources/CoreData/Shaders/GLSL/LitSolid.glsl

@@ -54,10 +54,10 @@ void VS()
     #endif
 
     #ifdef NORMALMAP
-        vec3 tangent = GetWorldTangent(modelMatrix);
-        vec3 bitangent = cross(tangent, vNormal) * iTangent.w;
+        vec4 tangent = GetWorldTangent(modelMatrix);
+        vec3 bitangent = cross(tangent.xyz, vNormal) * tangent.w;
         vTexCoord = vec4(GetTexCoord(iTexCoord), bitangent.xy);
-        vTangent = vec4(tangent, bitangent.z);
+        vTangent = vec4(tangent.xyz, bitangent.z);
     #else
         vTexCoord = GetTexCoord(iTexCoord);
     #endif

+ 96 - 5
Resources/CoreData/Shaders/GLSL/PBR.glsl

@@ -1,6 +1,81 @@
 #include "BRDF.glsl"
 #ifdef COMPILEPS
 
+    vec3 SphereLight(vec3 worldPos, vec3 lightVec, vec3 normal, vec3 toCamera, float roughness, vec3 specColor, out float ndl)
+    {
+        vec3 pos   = (cLightPosPS.xyz - worldPos);
+        float radius = cLightRad;
+
+        vec3 reflectVec   = reflect(-toCamera, normal);
+        vec3 centreToRay  = dot(pos, reflectVec) * reflectVec - pos;
+        vec3 closestPoint = pos + centreToRay * clamp(radius / length(centreToRay), 0.0, 1.0);
+
+        vec3 l = normalize(closestPoint);
+        vec3 h = normalize(toCamera + l);
+
+        ndl       = clamp(dot(normal, l), 0.0, 1.0);
+        float hdn = clamp(dot(h, normal), 0.0, 1.0);
+        float hdv = dot(h, toCamera);
+        float ndv = clamp(dot(normal, toCamera), 0.0, 1.0);
+
+        float distL      = length(pos);
+        float alpha      = roughness * roughness;
+        float alphaPrime = clamp(radius / (distL * 2.0) + alpha, 0.0, 1.0);
+
+        vec3 fresnelTerm = Fresnel(specColor, hdv) ;
+        float distTerm     = Distribution(hdn, alphaPrime);
+        float visTerm      = Visibility(ndl, ndv, roughness);
+
+        return distTerm * visTerm * fresnelTerm ;
+    }
+
+    vec3 TubeLight(vec3 worldPos, vec3 lightVec, vec3 normal, vec3 toCamera, float roughness, vec3 specColor, out float ndl)
+    {
+        float radius      = cLightRad;
+        float len         = cLightLength; 
+        vec3 pos         = (cLightPosPS.xyz - worldPos);
+        vec3 reflectVec  = reflect(-toCamera, normal);
+        
+        vec3 L01 = cLightDirPS * len;
+        vec3 L0 = pos - 0.5 * L01;
+        vec3 L1 = pos + 0.5 * L01;
+        vec3 ld = L1 - L0;
+
+        float distL0    = length( L0 );
+        float distL1    = length( L1 );
+
+        float NoL0      = dot( L0, normal ) / ( 2.0 * distL0 );
+        float NoL1      = dot( L1, normal ) / ( 2.0 * distL1 );
+        ndl             = ( 2.0 * clamp( NoL0 + NoL1, 0.0, 1.0 ) ) 
+                        / ( distL0 * distL1 + dot( L0, L1 ) + 2.0 );
+    
+        float a = len * len;
+        float b = dot( reflectVec, L01 );
+        float t = clamp( dot( L0, b * reflectVec - L01 ) / (a - b*b), 0.0, 1.0 );
+        
+        vec3 closestPoint   = L0 + ld * clamp(t, 0.0, 1.0);
+        vec3 centreToRay    = dot( closestPoint, reflectVec ) * reflectVec - closestPoint;
+        closestPoint          = closestPoint + centreToRay * clamp(radius / length(centreToRay), 0.0, 1.0);
+
+        vec3 l = normalize(closestPoint);
+        vec3 h = normalize(toCamera + l);
+
+        ndl       =  clamp(dot(normal, lightVec), 0.0, 1.0);
+        float hdn = clamp(dot(h, normal), 0.0, 1.0);
+        float hdv = dot(h, toCamera);
+        float ndv = clamp(dot(normal, toCamera), 0.0 ,1.0);
+
+        float distL      = length(closestPoint);
+        float alpha      = roughness * roughness;
+        float alphaPrime = clamp(radius / (distL * 2.0) + alpha, 0.0, 1.0);
+
+        vec3 fresnelTerm = Fresnel(specColor, hdv) ;
+        float distTerm     = Distribution(hdn, alphaPrime);
+        float visTerm      = Visibility(ndl, ndv, roughness);
+
+        return distTerm * visTerm * fresnelTerm ;
+    }
+
 	//Return the PBR BRDF value
 	// lightDir  = the vector to the light
 	// lightVev  = normalised lightDir
@@ -9,7 +84,7 @@
 	// roughness = roughness of the pixel
 	// diffColor = the rgb color of the pixel
 	// specColor = the rgb specular color of the pixel
-	vec3 GetBRDF(vec3 lightDir, vec3 lightVec, vec3 toCamera, vec3 normal, float roughness, vec3 diffColor, vec3 specColor)
+	vec3 GetBRDF(vec3 worldPos, vec3 lightDir, vec3 lightVec, vec3 toCamera, vec3 normal, float roughness, vec3 diffColor, vec3 specColor)
 	{
         vec3 Hn = normalize(toCamera + lightDir);
         float vdh = clamp((dot(toCamera, Hn)), M_EPSILON, 1.0);
@@ -21,11 +96,27 @@
         vec3 specularFactor = vec3(0.0, 0.0, 0.0);
 
         #ifdef SPECULAR
-            vec3 fresnelTerm = Fresnel(specColor, vdh) ;
-            float distTerm = Distribution(ndh, roughness);
-            float visTerm = Visibility(ndl, ndv, roughness);
+            if(cLightRad > 0.0)
+            {
+                if(cLightLength > 0.0)
+                {
+                    specularFactor = TubeLight(worldPos, lightVec, normal, toCamera, roughness, specColor, ndl);
+                    specularFactor *= ndl;
+                }
+                else
+                {
+                    specularFactor = SphereLight(worldPos, lightVec, normal, toCamera, roughness, specColor, ndl);
+                    specularFactor *= ndl;
+                }
+            }
+            else
+            {
+                vec3 fresnelTerm = Fresnel(specColor, vdh) ;
+                float distTerm = Distribution(ndh, roughness);
+                float visTerm = Visibility(ndl, ndv, roughness);
 
-            specularFactor = fresnelTerm * distTerm * visTerm  / M_PI;
+                specularFactor = fresnelTerm * distTerm * visTerm  / M_PI;
+            }
         #endif
 
         return diffuseFactor + specularFactor;

+ 11 - 3
Resources/CoreData/Shaders/GLSL/PBRDeferred.glsl

@@ -86,7 +86,16 @@ void PS()
     vec4 projWorldPos = vec4(worldPos, 1.0);
 
     vec3 lightDir;
-    float atten = GetAtten(normal, worldPos, lightDir);
+
+    float atten = 1;
+
+    #if defined(DIRLIGHT)
+        atten = GetAtten(normal, worldPos, lightDir);
+    #elif defined(SPOTLIGHT)
+        atten = GetAttenSpot(normal, worldPos, lightDir);
+    #else
+        atten = GetAttenPoint(normal, worldPos, lightDir);
+    #endif
 
     float shadow = 1;
     #ifdef SHADOW
@@ -108,8 +117,7 @@ void PS()
 
     float ndl = clamp(abs(dot(normal, lightVec)), M_EPSILON, 1.0);
 
-
-    vec3 BRDF = GetBRDF(lightDir, lightVec, toCamera, normal, roughness, albedoInput.rgb, specColor);
+    vec3 BRDF = GetBRDF(worldPos, lightDir, lightVec, toCamera, normal, roughness, albedoInput.rgb, specColor);
 
     gl_FragColor.a = 1.0;
     gl_FragColor.rgb = BRDF * lightColor * (atten * shadow) / M_PI;

+ 14 - 5
Resources/CoreData/Shaders/GLSL/PBRLitSolid.glsl

@@ -58,10 +58,10 @@ void VS()
     #endif
 
     #if defined(NORMALMAP) || defined(DIRBILLBOARD) || defined(IBL)
-        vec3 tangent = GetWorldTangent(modelMatrix);
-        vec3 bitangent = cross(tangent, vNormal) * iTangent.w;
+        vec4 tangent = GetWorldTangent(modelMatrix);
+        vec3 bitangent = cross(tangent.xyz, vNormal) * tangent.w;
         vTexCoord = vec4(GetTexCoord(iTexCoord), bitangent.xy);
-        vTangent = vec4(tangent, bitangent.z);
+        vTangent = vec4(tangent.xyz, bitangent.z);
     #else
         vTexCoord = GetTexCoord(iTexCoord);
     #endif
@@ -172,7 +172,16 @@ void PS()
         vec3 lightDir;
         vec3 finalColor;
 
-        float atten = GetAtten(normal, vWorldPos.xyz, lightDir);
+        float atten = 1;
+
+        #if defined(DIRLIGHT)
+            atten = GetAtten(normal, vWorldPos.xyz, lightDir);
+        #elif defined(SPOTLIGHT)
+            atten = GetAttenSpot(normal, vWorldPos.xyz, lightDir);
+        #else
+            atten = GetAttenPoint(normal, vWorldPos.xyz, lightDir);
+        #endif
+
         float shadow = 1.0;
         #ifdef SHADOW
             shadow = GetShadow(vShadowPos, vWorldPos.w);
@@ -189,7 +198,7 @@ void PS()
         vec3 lightVec = normalize(lightDir);
         float ndl = clamp((dot(normal, lightVec)), M_EPSILON, 1.0);
 
-        vec3 BRDF = GetBRDF(lightDir, lightVec, toCamera, normal, roughness, diffColor.rgb, specColor);
+        vec3 BRDF = GetBRDF(vWorldPos.xyz, lightDir, lightVec, toCamera, normal, roughness, diffColor.rgb, specColor);
 
         finalColor.rgb = BRDF * lightColor * (atten * shadow) / M_PI;
 

+ 3 - 3
Resources/CoreData/Shaders/GLSL/Shadow.glsl

@@ -3,7 +3,7 @@
 #include "Transform.glsl"
 
 #ifdef VSM_SHADOW
-    varying vec3 vTexCoord;
+    varying vec4 vTexCoord;
 #else
     varying vec2 vTexCoord;
 #endif
@@ -14,7 +14,7 @@ void VS()
     vec3 worldPos = GetWorldPos(modelMatrix);
     gl_Position = GetClipPos(worldPos);
     #ifdef VSM_SHADOW
-        vTexCoord = vec3(GetTexCoord(iTexCoord), gl_Position.z / gl_Position.w * 0.5 + 0.5);
+        vTexCoord = vec4(GetTexCoord(iTexCoord), gl_Position.z, gl_Position.w);
     #else
         vTexCoord = GetTexCoord(iTexCoord);
     #endif
@@ -29,7 +29,7 @@ void PS()
     #endif
 
     #ifdef VSM_SHADOW
-        float depth = vTexCoord.z;
+        float depth = vTexCoord.z / vTexCoord.w * 0.5 + 0.5;
         gl_FragColor = vec4(depth, depth * depth, 1.0, 1.0);
     #else
         gl_FragColor = vec4(1.0);

+ 82 - 24
Resources/CoreData/Shaders/GLSL/Text.glsl

@@ -19,46 +19,104 @@ void VS()
     mat4 modelMatrix = iModelMatrix;
     vec3 worldPos = GetWorldPos(modelMatrix);
     gl_Position = GetClipPos(worldPos);
-    
+
     vTexCoord = iTexCoord;
     vColor = iColor;
 }
 
-void PS()
-{
-    gl_FragColor.rgb = vColor.rgb;
+/*
+    1) Simplest SDF shader:
 
-#ifdef SIGNED_DISTANCE_FIELD
     float distance = texture2D(sDiffMap, vTexCoord).a;
-    if (distance < 0.5)
-    {
-    #ifdef TEXT_EFFECT_SHADOW
-        if (texture2D(sDiffMap, vTexCoord - cShadowOffset).a > 0.5)
-            gl_FragColor = cShadowColor;
-        else
-    #endif
-        gl_FragColor.a = 0.0;
-    }
+    if (distance >= 0.5)
+        gl_FragColor.a = vColor.a; // This is glyph
     else
+        gl_FragColor.a = 0.0; // Outside glyph
+
+    2) Glyph with antialiazed border:
+
+    float distance = texture2D(sDiffMap, vTexCoord).a;
+    gl_FragColor.a = vColor.a * smoothstep(0.495, 0.505, distance);
+
+    3) Quality improvement for far and small text:
+
+    float distance = texture2D(sDiffMap, vTexCoord).a;
+    // How much "distance" is changed for neighboring pixels.
+    // If text is far then width is big. Far text will be blurred.
+    float width = fwidth(distance);
+    gl_FragColor.a = vColor.a * smoothstep(0.5 - width, 0.5 + width, distance);
+*/
+
+#if defined(COMPILEPS) && defined(SIGNED_DISTANCE_FIELD)
+    float GetAlpha(float distance, float width)
     {
+        return smoothstep(0.5 - width, 0.5 + width, distance);
+    }
+
+    // Comment this define to turn off supersampling
+    #define SUPERSAMPLING
+#endif
+
+void PS()
+{
+#ifdef SIGNED_DISTANCE_FIELD
+    gl_FragColor.rgb = vColor.rgb;
+    float distance = texture2D(sDiffMap, vTexCoord).a;
+
     #ifdef TEXT_EFFECT_STROKE
-        if (distance < 0.525)
-            gl_FragColor.rgb = cStrokeColor.rgb;
+        #ifdef SUPERSAMPLING
+            float outlineFactor = smoothstep(0.5, 0.525, distance); // Border of glyph
+            gl_FragColor.rgb = mix(cStrokeColor.rgb, vColor.rgb, outlineFactor);
+        #else
+            if (distance < 0.525)
+               gl_FragColor.rgb = cStrokeColor.rgb;
+        #endif
     #endif
 
     #ifdef TEXT_EFFECT_SHADOW
-        if (texture2D(sDiffMap, vTexCoord + cShadowOffset).a < 0.5)
-            gl_FragColor.a = vColor.a;
+        if (texture2D(sDiffMap, vTexCoord - cShadowOffset).a > 0.5 && distance <= 0.5)
+            gl_FragColor = cShadowColor;
+        #ifndef SUPERSAMPLING
+        else if (distance <= 0.5)
+            gl_FragColor.a = 0;
+        #endif
         else
     #endif
-        gl_FragColor.a = vColor.a * smoothstep(0.5, 0.505, distance);
-    }
+        {
+            float width = fwidth(distance);
+            float alpha = GetAlpha(distance, width);
+
+            #ifdef SUPERSAMPLING
+                vec2 deltaUV = 0.354 * fwidth(vTexCoord); // (1.0 / sqrt(2.0)) / 2.0 = 0.354
+                vec4 square = vec4(vTexCoord - deltaUV, vTexCoord + deltaUV);
+
+                float distance2 = texture2D(sDiffMap, square.xy).a;
+                float distance3 = texture2D(sDiffMap, square.zw).a;
+                float distance4 = texture2D(sDiffMap, square.xw).a;
+                float distance5 = texture2D(sDiffMap, square.zy).a;
+
+                alpha += GetAlpha(distance2, width)
+                       + GetAlpha(distance3, width)
+                       + GetAlpha(distance4, width)
+                       + GetAlpha(distance5, width);
+
+                // For calculating of average correct would be dividing by 5.
+                // But when text is blurred, its brightness is lost. Therefore divide by 4.
+                alpha = alpha * 0.25;
+            #endif
+
+            gl_FragColor.a = alpha;
+        }
 #else
-    // Non-SDF font will likely be monochrome, in which case the alpha channel will be on the R channel on OpenGL 3
-    #ifdef GL3
-        gl_FragColor.a = vColor.a * texture2D(sDiffMap, vTexCoord).r;
+    #ifdef ALPHAMAP
+        gl_FragColor.rgb = vColor.rgb;
+        #ifdef GL3
+            gl_FragColor.a = vColor.a * texture2D(sDiffMap, vTexCoord).r;
+        #else
+            gl_FragColor.a = vColor.a * texture2D(sDiffMap, vTexCoord).a;
+        #endif
     #else
-        gl_FragColor.a = vColor.a * texture2D(sDiffMap, vTexCoord).a;
+        gl_FragColor = vColor * texture2D(sDiffMap, vTexCoord);
     #endif
 #endif
 }

+ 19 - 7
Resources/CoreData/Shaders/GLSL/Transform.glsl

@@ -147,7 +147,7 @@ vec3 GetTrailNormal(vec4 iPos, vec3 iParentPos, vec3 iForward)
 #if defined(SKINNED)
     #define iModelMatrix GetSkinMatrix(iBlendWeights, iBlendIndices)
 #elif defined(INSTANCED)
-    #define iModelMatrix GetInstanceMatrix();
+    #define iModelMatrix GetInstanceMatrix()
 #else
     #define iModelMatrix cModel
 #endif
@@ -182,9 +182,15 @@ vec3 GetWorldNormal(mat4 modelMatrix)
     #endif
 }
 
-vec3 GetWorldTangent(mat4 modelMatrix)
+vec4 GetWorldTangent(mat4 modelMatrix)
 {
-    return normalize(iTangent.xyz * GetNormalMatrix(modelMatrix));
+    #if defined(BILLBOARD)
+        return vec4(normalize(vec3(1.0, 0.0, 0.0) * cBillboardRot), 1.0);
+    #elif defined(DIRBILLBOARD)
+        return vec4(normalize(vec3(1.0, 0.0, 0.0) * GetNormalMatrix(modelMatrix)), 1.0);
+    #else
+        return vec4(normalize(iTangent.xyz * GetNormalMatrix(modelMatrix)), iTangent.w);
+    #endif
 }
 
 #else
@@ -193,15 +199,21 @@ vec3 GetWorldTangent(mat4 modelMatrix)
 #ifdef GL3
 #define varying in
 
-// \todo: should not hardcode the number of MRT outputs according to defines
+#ifndef MRT_COUNT
+
 #if defined(DEFERRED)
-out vec4 fragData[4];
+#define MRT_COUNT 4
 #elif defined(PREPASS)
-out vec4 fragData[2];
+#define MRT_COUNT 2
 #else
-out vec4 fragData[1];
+#define MRT_COUNT 1
 #endif
 
+#endif
+
+out vec4 fragData[MRT_COUNT];
+
+
 #define gl_FragColor fragData[0]
 #define gl_FragData fragData
 #endif

+ 14 - 4
Resources/CoreData/Shaders/GLSL/Uniforms.glsl

@@ -71,7 +71,11 @@ uniform vec4 cMatSpecColor;
 #ifdef PBR
     uniform float cRoughness;
     uniform float cMetallic;
+    uniform float cLightRad;
+    uniform float cLightLength;
 #endif
+uniform vec3 cZoneMin;
+uniform vec3 cZoneMax;
 uniform float cNearClipPS;
 uniform float cFarClipPS;
 uniform vec4 cShadowCubeAdjust;
@@ -174,6 +178,8 @@ uniform ZonePS
     vec4 cAmbientColor;
     vec4 cFogParams;
     vec3 cFogColor;
+    vec3 cZoneMin;
+    vec3 cZoneMax;
 };
 
 uniform LightPS
@@ -191,6 +197,10 @@ uniform LightPS
 #ifdef VSM_SHADOW
     vec2 cVSMShadowParams;
 #endif
+#ifdef PBR
+    float cLightRad;
+    float cLightLength;
+#endif
 };
 
 #ifndef CUSTOM_MATERIAL_CBUFFER
@@ -200,10 +210,10 @@ uniform MaterialPS
     vec3 cMatEmissiveColor;
     vec3 cMatEnvMapColor;
     vec4 cMatSpecColor;
-    #ifdef PBR
-        float cRoughness;
-        float cMetallic;
-    #endif
+#ifdef PBR
+    float cRoughness;
+    float cMetallic;
+#endif
 };
 #endif
 

+ 0 - 3
Resources/CoreData/Shaders/GLSL/Unlit.glsl

@@ -20,9 +20,6 @@ void VS()
 
     #ifdef VERTEXCOLOR
         vColor = iColor;
-        //#ifdef TRAIL
-        //    vColor = vec4(normalize(cCameraPos), 1.0);
-        //#endif
     #endif
 
 }

+ 90 - 0
Resources/CoreData/Shaders/GLSL/UnlitParticle.glsl

@@ -0,0 +1,90 @@
+#include "Uniforms.glsl"
+#include "Samplers.glsl"
+#include "Transform.glsl"
+#include "ScreenPos.glsl"
+#include "Fog.glsl"
+
+varying vec2 vTexCoord;
+varying vec4 vWorldPos;
+#ifdef VERTEXCOLOR
+    varying vec4 vColor;
+#endif
+#ifdef SOFTPARTICLES
+    varying vec4 vScreenPos;
+    uniform float cSoftParticleFadeScale;
+#endif
+
+void VS()
+{
+    mat4 modelMatrix = iModelMatrix;
+    vec3 worldPos = GetWorldPos(modelMatrix);
+    gl_Position = GetClipPos(worldPos);
+    vTexCoord = GetTexCoord(iTexCoord);
+    vWorldPos = vec4(worldPos, GetDepth(gl_Position));
+
+    #ifdef SOFTPARTICLES
+        vScreenPos = GetScreenPos(gl_Position);
+    #endif
+
+    #ifdef VERTEXCOLOR
+        vColor = iColor;
+    #endif
+
+}
+
+void PS()
+{
+    // Get material diffuse albedo
+    #ifdef DIFFMAP
+        vec4 diffColor = cMatDiffColor * texture2D(sDiffMap, vTexCoord);
+        #ifdef ALPHAMASK
+            if (diffColor.a < 0.5)
+                discard;
+        #endif
+    #else
+        vec4 diffColor = cMatDiffColor;
+    #endif
+
+    #ifdef VERTEXCOLOR
+        diffColor *= vColor;
+    #endif
+
+    // Get fog factor
+    #ifdef HEIGHTFOG
+        float fogFactor = GetHeightFogFactor(vWorldPos.w, vWorldPos.y);
+    #else
+        float fogFactor = GetFogFactor(vWorldPos.w);
+    #endif
+
+    // Soft particle fade
+    // In expand mode depth test should be off. In that case do manual alpha discard test first to reduce fill rate
+    #ifdef SOFTPARTICLES
+        #ifdef EXPAND
+            if (diffColor.a < 0.01)
+                discard;
+        #endif
+
+        float particleDepth = vWorldPos.w;
+        #ifdef HWDEPTH
+            float depth = ReconstructDepth(texture2DProj(sDepthBuffer, vScreenPos).r);
+        #else
+            float depth = DecodeDepth(texture2DProj(sDepthBuffer, vScreenPos).rgb);
+        #endif
+
+        #ifdef EXPAND
+            float diffZ = max(particleDepth - depth, 0.0) * (cFarClipPS - cNearClipPS);
+            float fade = clamp(diffZ * cSoftParticleFadeScale, 0.0, 1.0);
+        #else
+            float diffZ = (depth - particleDepth) * (cFarClipPS - cNearClipPS);
+            float fade = clamp(1.0 - diffZ * cSoftParticleFadeScale, 0.0, 1.0);
+        #endif
+
+        #ifndef ADDITIVE
+            diffColor.a = max(diffColor.a - fade, 0.0);
+        #else
+            diffColor.rgb = max(diffColor.rgb - fade, vec3(0.0, 0.0, 0.0));
+        #endif
+    #endif
+
+    gl_FragColor = vec4(GetFog(diffColor.rgb, fogFactor), diffColor.a);
+}

+ 7 - 7
Resources/CoreData/Shaders/GLSL/Vegetation.glsl

@@ -48,7 +48,7 @@ void VS()
 {
     mat4 modelMatrix = iModelMatrix;
     vec3 worldPos = GetWorldPos(modelMatrix);
-    float height = worldPos.y - cModel[1][3];
+    float height = worldPos.y - modelMatrix[1][3];
 
     float windStrength = max(height - cWindHeightPivot, 0.0) * cWindHeightFactor;
     float windPeriod = cElapsedTime * cWindPeriod + dot(worldPos.xz, cWindWorldSpacing);
@@ -64,10 +64,10 @@ void VS()
     #endif
 
     #ifdef NORMALMAP
-        vec3 tangent = GetWorldTangent(modelMatrix);
-        vec3 bitangent = cross(tangent, vNormal) * iTangent.w;
+        vec4 tangent = GetWorldTangent(modelMatrix);
+        vec3 bitangent = cross(tangent.xyz, vNormal) * tangent.w;
         vTexCoord = vec4(GetTexCoord(iTexCoord), bitangent.xy);
-        vTangent = vec4(tangent, bitangent.z);
+        vTangent = vec4(tangent.xyz, bitangent.z);
     #else
         vTexCoord = GetTexCoord(iTexCoord);
     #endif
@@ -86,7 +86,7 @@ void VS()
             // Spotlight projection: transform from world space to projector texture coordinates
             vSpotPos = projWorldPos * cLightMatrices[0];
         #endif
-    
+
         #ifdef POINTLIGHT
             vCubeMaskVec = (worldPos - cLightPos.xyz) * mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz);
         #endif
@@ -100,12 +100,12 @@ void VS()
         #else
             vVertexLight = GetAmbient(GetZonePos(worldPos));
         #endif
-        
+
         #ifdef NUMVERTEXLIGHTS
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
                 vVertexLight += GetVertexLight(i, worldPos, vNormal) * cVertexLights[i * 3].rgb;
         #endif
-        
+
         vScreenPos = GetScreenPos(gl_Position);
 
         #ifdef ENVCUBEMAP

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

@@ -32,7 +32,7 @@ void PS(
     out float4 oColor : OUTCOLOR0)
 {
     #ifdef ALPHAMASK
-        float alpha = Sample2D(sDiffMap, iTexCoord.xy).a;
+        float alpha = Sample2D(DiffMap, iTexCoord.xy).a;
         if (alpha < 0.5)
             discard;
     #endif

+ 37 - 14
Resources/CoreData/Shaders/HLSL/IBL.hlsl

@@ -227,23 +227,35 @@
         return lerp(normal, reflection, lerpFactor);
     }
 
-    float GetMipFromRougness(float roughness)
+    float GetMipFromRoughness(float roughness)
     {
-        const float smoothness = 1.0 - roughness;
-        return (1.0 - smoothness * smoothness) * 10.0;
+        float Level = 3 - 1.15 * log2( roughness );
+        return 9.0 - 1 - Level;
     }
 
 
-    float3 EnvBRDFApprox (float3 SpecularColor, float Roughness, float NoV)
+    float3 EnvBRDFApprox (float3 specColor, float roughness, float ndv)
     {
         const float4 c0 = float4(-1, -0.0275, -0.572, 0.022 );
         const float4 c1 = float4(1, 0.0425, 1.0, -0.04 );
-        float4 r = Roughness * c0 + c1;
-        float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
+        float4 r = roughness * c0 + c1;
+        float a004 = min( r.x * r.x, exp2( -9.28 * ndv ) ) * r.x + r.y;
         float2 AB = float2( -1.04, 1.04 ) * a004 + r.zw;
-        return SpecularColor * AB.x + AB.y;
+        return specColor * AB.x + AB.y;
     }
 
+    float3 FixCubeLookup(float3 v) 
+    {
+        float M = max(max(abs(v.x), abs(v.y)), abs(v.z));
+        float scale = (1024 - 1) / 1024;
+
+        if (abs(v.x) != M) v.x += scale;
+        if (abs(v.y) != M) v.y += scale;
+        if (abs(v.z) != M) v.z += scale; 
+
+        return v;
+    }
+    
     /// Calculate IBL contributation
     ///     reflectVec: reflection vector for cube sampling
     ///     wsNormal: surface normal in word space
@@ -251,17 +263,28 @@
     ///     roughness: surface roughness
     ///     ambientOcclusion: ambient occlusion
     float3 ImageBasedLighting(in float3 reflectVec, in float3 tangent, in float3 bitangent, in float3 wsNormal, in float3 toCamera, in float3 diffColor, in float3 specColor, in float roughness, inout float3 reflectionCubeColor)
-    {
+    { 
         reflectVec = GetSpecularDominantDir(wsNormal, reflectVec, roughness);
         const float ndv = saturate(dot(-toCamera, wsNormal));
 
-        // PMREM Mipmapmode https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/
-        //const float GlossScale = 16.0;
-        //const float GlossBias = 5.0;
-        const float mipSelect = roughness * 9.0;// exp2(GlossScale * roughness + GlossBias) - exp2(GlossBias);
+        /// Test: Parallax correction, currently not working
+
+        // float3 intersectMax = (cZoneMax - toCamera) / reflectVec;
+        // float3 intersectMin = (cZoneMin - toCamera) / reflectVec;
+        
+        // float3 furthestPlane = max(intersectMax, intersectMin);
+        
+        // float planeDistance = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z);
+
+        // // Get the intersection position
+        // float3 intersectionPos = toCamera + reflectVec * planeDistance;
+        // // Get corrected reflection
+        // reflectVec = intersectionPos - ((cZoneMin + cZoneMax )/ 2);
 
-        float3 cube = SampleCubeLOD(ZoneCubeMap, float4(reflectVec, mipSelect)).rgb;
-        float3 cubeD = SampleCubeLOD(ZoneCubeMap, float4(wsNormal, 9.0)).rgb;
+        const float mipSelect = GetMipFromRoughness(roughness);
+        float3 cube = SampleCubeLOD(ZoneCubeMap, float4(FixCubeLookup(reflectVec), mipSelect)).rgb;
+        float3 cubeD = SampleCubeLOD(ZoneCubeMap, float4(FixCubeLookup(wsNormal), 9.0)).rgb;
+        
         // Fake the HDR texture
         float brightness = clamp(cAmbientColor.a, 0.0, 1.0);
         float darknessCutoff = clamp((cAmbientColor.a - 1.0) * 0.1, 0.0, 0.25);

+ 24 - 10
Resources/CoreData/Shaders/HLSL/Lighting.hlsl

@@ -138,19 +138,33 @@ float GetDiffuse(float3 normal, float3 worldPos, out float3 lightDir)
 
 float GetAtten(float3 normal, float3 worldPos, out float3 lightDir)
 {
-     #ifdef DIRLIGHT
-        lightDir = cLightDirPS;
-        return saturate(dot(normal, lightDir));
-    #else
-        float3 lightVec = (cLightPosPS.xyz - worldPos) * cLightPosPS.w;
-        float lightDist = length(lightVec);
-        float falloff = pow(saturate(1.0 - pow(lightDist / 1.0, 4.0)), 2.0) / (pow(lightDist, 2.0) + 1.0);
+    lightDir = cLightDirPS;
+    return saturate(dot(normal, lightDir));
+    
+}
+
+float GetAttenPoint(float3 normal, float3 worldPos, out float3 lightDir)
+{
+    float3 lightVec = (cLightPosPS.xyz - worldPos) * cLightPosPS.w;
+    float lightDist = length(lightVec);
+    float falloff = pow(saturate(1.0 - pow(lightDist / 1.0, 4.0)), 2.0) * 3.14159265358979323846 / (4 * 3.14159265358979323846)*(pow(lightDist, 2.0) + 1.0);
+    lightDir = lightVec / lightDist;
+    return saturate(dot(normal, lightDir)) * falloff;
+
+}
+
+float GetAttenSpot(float3 normal, float3 worldPos, out float3 lightDir)
+{
+    float3 lightVec = (cLightPosPS.xyz - worldPos) * cLightPosPS.w;
+    float lightDist = length(lightVec);
+    float falloff = pow(saturate(1.0 - pow(lightDist / 1.0, 4.0)), 2.0) / (pow(lightDist, 2.0) + 1.0);
+
+    lightDir = lightVec / lightDist;
+    return saturate(dot(normal, lightDir)) * falloff;
 
-        lightDir = lightVec / lightDist;
-        return saturate(dot(normal, lightDir)) * falloff;
-    #endif
 }
 
+
 float GetDiffuseVolumetric(float3 worldPos)
 {
     #ifdef DIRLIGHT

+ 49 - 0
Resources/CoreData/Shaders/HLSL/LitParticle.hlsl

@@ -2,8 +2,22 @@
 #include "Samplers.hlsl"
 #include "Transform.hlsl"
 #include "Lighting.hlsl"
+#include "ScreenPos.hlsl"
 #include "Fog.hlsl"
 
+#if defined(COMPILEPS) && defined(SOFTPARTICLES)
+#ifndef D3D11
+// D3D9 uniform
+uniform float cSoftParticleFadeScale;
+#else
+// D3D11 constant buffer
+cbuffer CustomPS : register(b6)
+{
+    float cSoftParticleFadeScale;
+}
+#endif
+#endif
+
 void VS(float4 iPos : POSITION,
     #if !defined(BILLBOARD) && !defined(TRAILFACECAM)
         float3 iNormal : NORMAL,
@@ -28,6 +42,9 @@ void VS(float4 iPos : POSITION,
         float4 iTangent : TANGENT,
     #endif
     out float2 oTexCoord : TEXCOORD0,
+    #ifdef SOFTPARTICLES
+        out float4 oScreenPos : TEXCOORD1,
+    #endif
     out float4 oWorldPos : TEXCOORD3,
     #if PERPIXEL
         #ifdef SHADOW
@@ -65,6 +82,10 @@ void VS(float4 iPos : POSITION,
         oClip = dot(oPos, cClipPlane);
     #endif
 
+    #ifdef SOFTPARTICLES
+        oScreenPos = GetScreenPos(oPos);
+    #endif
+
     #ifdef VERTEXCOLOR
         oColor = iColor;
     #endif
@@ -98,6 +119,9 @@ void VS(float4 iPos : POSITION,
 }
 
 void PS(float2 iTexCoord : TEXCOORD0,
+    #ifdef SOFTPARTICLES
+        float4 iScreenPos: TEXCOORD1,
+    #endif
     float4 iWorldPos : TEXCOORD3,
     #ifdef PERPIXEL
         #ifdef SHADOW
@@ -143,6 +167,31 @@ void PS(float2 iTexCoord : TEXCOORD0,
         float fogFactor = GetFogFactor(iWorldPos.w);
     #endif
 
+    // Soft particle fade
+    // In expand mode depth test should be off. In that case do manual alpha discard test first to reduce fill rate
+    #ifdef SOFTPARTICLES
+        #ifdef EXPAND
+            if (diffColor.a < 0.01)
+                discard;
+        #endif
+
+        float particleDepth = iWorldPos.w;
+        float depth = Sample2DProj(DepthBuffer, iScreenPos).r;
+        #ifdef HWDEPTH
+            depth = ReconstructDepth(depth);
+        #endif
+
+        #ifdef EXPAND
+            float diffZ = max(particleDepth - depth, 0.0) * (cFarClipPS - cNearClipPS);
+            float fade = saturate(diffZ * cSoftParticleFadeScale);
+        #else
+            float diffZ = (depth - particleDepth) * (cFarClipPS - cNearClipPS);
+            float fade = saturate(1.0 - diffZ * cSoftParticleFadeScale);
+        #endif
+
+        diffColor.a = max(diffColor.a - fade, 0.0);
+    #endif
+
     #ifdef PERPIXEL
         // Per-pixel forward lighting
         float3 lightColor;

+ 4 - 4
Resources/CoreData/Shaders/HLSL/LitSolid.hlsl

@@ -18,7 +18,7 @@ void VS(float4 iPos : POSITION,
     #if defined(LIGHTMAP) || defined(AO)
         float2 iTexCoord2 : TEXCOORD1,
     #endif
-    #if defined(NORMALMAP) || defined(TRAILFACECAM) || defined(TRAILBONE)
+    #if (defined(NORMALMAP) || defined(TRAILFACECAM) || defined(TRAILBONE)) && !defined(BILLBOARD) && !defined(DIRBILLBOARD)
         float4 iTangent : TANGENT,
     #endif
     #ifdef SKINNED
@@ -87,10 +87,10 @@ void VS(float4 iPos : POSITION,
     #endif
 
     #ifdef NORMALMAP
-        float3 tangent = GetWorldTangent(modelMatrix);
-        float3 bitangent = cross(tangent, oNormal) * iTangent.w;
+        float4 tangent = GetWorldTangent(modelMatrix);
+        float3 bitangent = cross(tangent.xyz, oNormal) * tangent.w;
         oTexCoord = float4(GetTexCoord(iTexCoord), bitangent.xy);
-        oTangent = float4(tangent, bitangent.z);
+        oTangent = float4(tangent.xyz, bitangent.z);
     #else
         oTexCoord = GetTexCoord(iTexCoord);
     #endif

+ 101 - 8
Resources/CoreData/Shaders/HLSL/PBR.hlsl

@@ -1,32 +1,125 @@
 #include "BRDF.hlsl"
 #ifdef COMPILEPS
 
+    
+
+    float3 SphereLight(float3 worldPos, float3 lightVec, float3 normal, float3 toCamera, float roughness, float3 specColor, out float ndl)
+    {
+        float3 pos   = (cLightPosPS.xyz - worldPos);
+        float radius = cLightRad;
+
+        float3 reflectVec   = reflect(-toCamera, normal);
+        float3 centreToRay  = dot(pos, reflectVec) * reflectVec - pos;
+        float3 closestPoint = pos + centreToRay * saturate(radius / length(centreToRay));
+
+        float3 l = normalize(closestPoint);
+        float3 h = normalize(toCamera + l);
+
+        ndl       = saturate(dot(normal, l));
+        float hdn = saturate(dot(h, normal));
+        float hdv = dot(h, toCamera);
+        float ndv = saturate(dot(normal, toCamera));
+
+        float distL      = length(pos);
+        float alpha      = roughness * roughness;
+        float alphaPrime = saturate(radius / (distL * 2.0) + alpha);
+
+        const float3 fresnelTerm = Fresnel(specColor, hdv) ;
+        const float distTerm     = Distribution(hdn, alphaPrime);
+        const float visTerm      = Visibility(ndl, ndv, roughness);
+
+        return distTerm * visTerm * fresnelTerm ;
+    }
+
+    float3 TubeLight(float3 worldPos, float3 lightVec, float3 normal, float3 toCamera, float roughness, float3 specColor, out float ndl)
+    {
+         float radius      = cLightRad;
+         float len         = cLightLength; 
+        float3 pos         = (cLightPosPS.xyz - worldPos);
+        float3 reflectVec  = reflect(-toCamera, normal);
+        
+        float3 L01 = cLightDirPS * len;
+        float3 L0 = pos - 0.5 * L01;
+        float3 L1 = pos + 0.5 * L01;
+        float3 ld = L1 - L0;
+
+        float distL0    = length( L0 );
+        float distL1    = length( L1 );
+
+        float NoL0      = dot( L0, normal ) / ( 2.0 * distL0 );
+        float NoL1      = dot( L1, normal ) / ( 2.0 * distL1 );
+        ndl             = ( 2.0 * clamp( NoL0 + NoL1, 0.0, 1.0 ) ) 
+                        / ( distL0 * distL1 + dot( L0, L1 ) + 2.0 );
+    
+        float a = len * len;
+        float b = dot( reflectVec, L01 );
+        float t = saturate( dot( L0, b * reflectVec - L01 ) / (a - b*b) );
+        
+        float3 closestPoint   = L0 + ld * saturate( t);
+        float3 centreToRay    = dot( closestPoint, reflectVec ) * reflectVec - closestPoint;
+        closestPoint          = closestPoint + centreToRay * saturate(radius / length(centreToRay));
+
+        float3 l = normalize(closestPoint);
+        float3 h = normalize(toCamera + l);
+
+        ndl       =  saturate(dot(normal, lightVec));
+        float hdn = saturate(dot(h, normal));
+        float hdv = dot(h, toCamera);
+        float ndv = saturate(dot(normal, toCamera));
+
+        float distL      = length(closestPoint);
+        float alpha      = roughness * roughness;
+        float alphaPrime = saturate(radius / (distL * 2.0) + alpha);
+
+        const float3 fresnelTerm = Fresnel(specColor, hdv) ;
+        const float distTerm     = Distribution(hdn, alphaPrime);
+        const float visTerm      = Visibility(ndl, ndv, roughness);
+
+        return distTerm * visTerm * fresnelTerm ;
+    }
+
 	//Return the PBR BRDF value
 	// lightDir  = the vector to the light
-	// lightVev  = normalised lightDir
+	// lightVec  = normalised lightDir
 	// toCamera  = vector to the camera
 	// normal    = surface normal of the pixel
 	// roughness = roughness of the pixel
 	// diffColor = the rgb color of the pixel
 	// specColor = the rgb specular color of the pixel
-	float3 GetBRDF(float3 lightDir, float3 lightVec, float3 toCamera, float3 normal, float roughness, float3 diffColor, float3 specColor)
+	float3 GetBRDF(float3 worldPos, float3 lightDir, float3 lightVec, float3 toCamera, float3 normal, float roughness, float3 diffColor, float3 specColor)
 	{
 
         const float3 Hn = normalize(toCamera + lightDir);
         const float vdh = clamp((dot(toCamera, Hn)), M_EPSILON, 1.0);
         const float ndh = clamp((dot(normal, Hn)), M_EPSILON, 1.0);
-        const float ndl = clamp((dot(normal, lightVec)), M_EPSILON, 1.0);
+        float ndl = clamp((dot(normal, lightVec)), M_EPSILON, 1.0);
         const float ndv = clamp((dot(normal, toCamera)), M_EPSILON, 1.0);
 
-        const float3 diffuseFactor = Diffuse(diffColor, roughness, ndv, ndl, vdh);
+        const float3 diffuseFactor = Diffuse(diffColor, roughness, ndv, ndl, vdh)  * ndl;
         float3 specularFactor = 0;
 
         #ifdef SPECULAR
-            const float3 fresnelTerm = Fresnel(specColor, vdh) ;
-            const float distTerm = Distribution(ndh, roughness);
-            const float visTerm = Visibility(ndl, ndv, roughness);
+            if(cLightRad > 0.0)
+            {
+                if(cLightLength > 0.0)
+                {
+                    specularFactor = TubeLight(worldPos, lightVec, normal, toCamera, roughness, specColor, ndl);
+                    specularFactor *= ndl;
+                }
+                else
+                {
+                    specularFactor = SphereLight(worldPos, lightVec, normal, toCamera, roughness, specColor, ndl);
+                    specularFactor *= ndl;
+                }
+            }
+            else
+            {
+                const float3 fresnelTerm = Fresnel(specColor, vdh) ;
+                const float distTerm = Distribution(ndh, roughness);
+                const float visTerm = Visibility(ndl, ndv, roughness);
+                specularFactor = distTerm * visTerm * fresnelTerm * ndl/ M_PI;
+            }
 
-            specularFactor = distTerm * visTerm * fresnelTerm / M_PI;
         #endif
 
         return diffuseFactor + specularFactor;

+ 10 - 2
Resources/CoreData/Shaders/HLSL/PBRDeferred.hlsl

@@ -93,7 +93,15 @@ void PS(
     const float4 projWorldPos = float4(worldPos, 1.0);
 
     float3 lightDir;
-    float atten = GetAtten(normal, worldPos, lightDir);
+     float atten = 1;
+
+        #if defined(DIRLIGHT)
+            atten = GetAtten(normal, worldPos, lightDir);
+        #elif defined(SPOTLIGHT)
+            atten = GetAttenSpot(normal, worldPos, lightDir);
+        #else
+            atten = GetAttenPoint(normal, worldPos, lightDir);
+        #endif
 
     float shadow = 1;
     #ifdef SHADOW
@@ -113,7 +121,7 @@ void PS(
     const float3 lightVec = normalize(lightDir);
     const float ndl = clamp(abs(dot(normal, lightVec)), M_EPSILON, 1.0);
 
-    float3 BRDF = GetBRDF(lightDir, lightVec, toCamera, normal, roughness, albedoInput.rgb, specColor);
+    float3 BRDF = GetBRDF(worldPos, lightDir, lightVec, toCamera, normal, roughness, albedoInput.rgb, specColor);
 
     oColor.a = 1;
     oColor.rgb  = BRDF * lightColor * shadow * atten / M_PI;

+ 15 - 8
Resources/CoreData/Shaders/HLSL/PBRLitSolid.hlsl

@@ -1,9 +1,9 @@
 #include "Uniforms.hlsl"
 #include "Samplers.hlsl"
+#include "Constants.hlsl"
 #include "Transform.hlsl"
 #include "ScreenPos.hlsl"
 #include "Lighting.hlsl"
-#include "Constants.hlsl"
 #include "Fog.hlsl"
 #include "PBR.hlsl"
 #include "IBL.hlsl"
@@ -21,7 +21,7 @@ void VS(float4 iPos : POSITION,
     #if defined(LIGHTMAP) || defined(AO)
         float2 iTexCoord2 : TEXCOORD1,
     #endif
-    #if defined(NORMALMAP)|| defined(IBL) || defined(TRAILFACECAM) || defined(TRAILBONE)
+    #if (defined(NORMALMAP)|| defined(IBL) || defined(TRAILFACECAM) || defined(TRAILBONE)) && !defined(BILLBOARD) && !defined(DIRBILLBOARD)
         float4 iTangent : TANGENT,
     #endif
     #ifdef SKINNED
@@ -90,10 +90,10 @@ void VS(float4 iPos : POSITION,
     #endif
 
     #if defined(NORMALMAP) || defined(IBL)
-        const float3 tangent = GetWorldTangent(modelMatrix);
-        const float3 bitangent = cross(tangent, oNormal) * iTangent.w;
+        const float4 tangent = GetWorldTangent(modelMatrix);
+        const float3 bitangent = cross(tangent.xyz, oNormal) * tangent.w;
         oTexCoord = float4(GetTexCoord(iTexCoord), bitangent.xy);
-        oTangent = float4(tangent, bitangent.z);
+        oTangent = float4(tangent.xyz, bitangent.z);
     #else
         oTexCoord = GetTexCoord(iTexCoord);
     #endif
@@ -226,7 +226,7 @@ void PS(
     diffColor.rgb = diffColor.rgb - diffColor.rgb * metalness; // Modulate down the diffuse
 
     // Get normal
-    #if defined(NORMALMAP) || defined(DIRBILLBOARD) || defined(IBL)
+    #if defined(NORMALMAP) || defined(IBL)
         const float3 tangent = normalize(iTangent.xyz);
         const float3 bitangent = normalize(float3(iTexCoord.zw, iTangent.w));
         const float3x3 tbn = float3x3(tangent, bitangent, iNormal);
@@ -252,8 +252,15 @@ void PS(
         float3 lightDir;
         float3 lightColor;
         float3 finalColor;
+        float atten = 1;
 
-        float atten = GetAtten(normal, iWorldPos.xyz, lightDir);
+        #if defined(DIRLIGHT)
+            atten = GetAtten(normal, iWorldPos.xyz, lightDir);
+        #elif defined(SPOTLIGHT)
+            atten = GetAttenSpot(normal, iWorldPos.xyz, lightDir);
+        #else
+            atten = GetAttenPoint(normal, iWorldPos.xyz, lightDir);
+        #endif
 
         float shadow = 1.0;
 
@@ -275,7 +282,7 @@ void PS(
         const float ndl = clamp((dot(normal, lightVec)), M_EPSILON, 1.0);
 
 
-        float3 BRDF = GetBRDF(lightDir, lightVec, toCamera, normal, roughness, diffColor.rgb, specColor);
+        float3 BRDF = GetBRDF(iWorldPos.xyz, lightDir, lightVec, toCamera, normal, roughness, diffColor.rgb, specColor);
         finalColor.rgb = BRDF * lightColor * (atten * shadow) / M_PI;
 
         #ifdef AMBIENT

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

@@ -3,6 +3,9 @@
 #include "Transform.hlsl"
 
 void VS(float4 iPos : POSITION,
+    #ifndef NOUV
+        float2 iTexCoord : TEXCOORD0,
+    #endif
     #ifdef SKINNED
         float4 iBlendWeights : BLENDWEIGHT,
         int4 iBlendIndices : BLENDINDICES,
@@ -10,11 +13,11 @@ void VS(float4 iPos : POSITION,
     #ifdef INSTANCED
         float4x3 iModelInstance : TEXCOORD4,
     #endif
-    #ifndef NOUV
-        float2 iTexCoord : TEXCOORD0,
+    #if defined(BILLBOARD) || defined(DIRBILLBOARD)
+        float2 iSize : TEXCOORD1,
     #endif
     #ifdef VSM_SHADOW
-        out float3 oTexCoord : TEXCOORD0,
+        out float4 oTexCoord : TEXCOORD0,
     #else
         out float2 oTexCoord : TEXCOORD0,
     #endif
@@ -29,7 +32,7 @@ void VS(float4 iPos : POSITION,
     float3 worldPos = GetWorldPos(modelMatrix);
     oPos = GetClipPos(worldPos);
     #ifdef VSM_SHADOW
-        oTexCoord = float3(GetTexCoord(iTexCoord), oPos.z/oPos.w);
+        oTexCoord = float4(GetTexCoord(iTexCoord), oPos.z, oPos.w);
     #else
         oTexCoord = GetTexCoord(iTexCoord);
     #endif
@@ -37,7 +40,7 @@ void VS(float4 iPos : POSITION,
 
 void PS(
     #ifdef VSM_SHADOW
-        float3 iTexCoord : TEXCOORD0,
+        float4 iTexCoord : TEXCOORD0,
     #else
         float2 iTexCoord : TEXCOORD0,
     #endif
@@ -50,7 +53,7 @@ void PS(
     #endif
 
     #ifdef VSM_SHADOW
-        float depth = iTexCoord.z;
+        float depth = iTexCoord.z / iTexCoord.w;
         oColor = float4(depth, depth * depth, 1.0, 1.0);
     #else
         oColor = 1.0;

+ 3 - 0
Resources/CoreData/Shaders/HLSL/Skybox.hlsl

@@ -3,6 +3,9 @@
 #include "Transform.hlsl"
 
 void VS(float4 iPos : POSITION,
+    #ifdef INSTANCED
+        float4x3 iModelInstance : TEXCOORD4,
+    #endif
     out float3 oTexCoord : TEXCOORD0,
     out float4 oPos : OUTPOSITION)
 {

+ 59 - 21
Resources/CoreData/Shaders/HLSL/Text.hlsl

@@ -37,38 +37,76 @@ void VS(float4 iPos : POSITION,
     oTexCoord = iTexCoord;
 }
 
+// See notes in GLSL shader
+#if defined(COMPILEPS) && defined(SIGNED_DISTANCE_FIELD)
+    float GetAlpha(float distance, float width)
+    {
+        return smoothstep(0.5 - width, 0.5 + width, distance);
+    }
+
+    // Comment this define to turn off supersampling
+    #define SUPERSAMPLING
+#endif
+
+
 void PS(float2 iTexCoord : TEXCOORD0,
     float4 iColor : COLOR0,
     out float4 oColor : OUTCOLOR0)
 {
-    oColor.rgb = iColor.rgb;
-
 #ifdef SIGNED_DISTANCE_FIELD
+    oColor.rgb = iColor.rgb;
     float distance = Sample2D(DiffMap, iTexCoord).a;
-    if (distance < 0.5f)
-    {
-    #ifdef TEXT_EFFECT_SHADOW
-        if (Sample2D(DiffMap, iTexCoord - cShadowOffset).a > 0.5f)
-            oColor = cShadowColor;
-        else
-    #endif
-        oColor.a = 0.0f;
-    }
-    else
-    {
+
     #ifdef TEXT_EFFECT_STROKE
-        if (distance < 0.525f)
-            oColor.rgb = cStrokeColor.rgb;
+        #ifdef SUPERSAMPLING
+            float outlineFactor = smoothstep(0.5, 0.525, distance); // Border of glyph
+            oColor.rgb = lerp(cStrokeColor.rgb, iColor.rgb, outlineFactor);
+        #else
+            if (distance < 0.525)
+                oColor.rgb = cStrokeColor.rgb;
+        #endif
     #endif
 
     #ifdef TEXT_EFFECT_SHADOW
-        if (Sample2D(DiffMap, iTexCoord + cShadowOffset).a < 0.5f)
-            oColor.a = iColor.a;
+        if (Sample2D(DiffMap, iTexCoord - cShadowOffset).a > 0.5 && distance <= 0.5)
+            oColor = cShadowColor;
+        #ifndef SUPERSAMPLING
+        else if (distance <= 0.5)
+            oColor.a = 0.0;
+        #endif
         else
     #endif
-        oColor.a = iColor.a * smoothstep(0.5f, 0.505f, distance);
-    }
+        {
+            float width = fwidth(distance);
+            float alpha = GetAlpha(distance, width);
+
+            #ifdef SUPERSAMPLING
+                float2 deltaUV = 0.354 * fwidth(iTexCoord); // (1.0 / sqrt(2.0)) / 2.0 = 0.354
+                float4 square = float4(iTexCoord - deltaUV, iTexCoord + deltaUV);
+
+                float distance2 = Sample2D(DiffMap, square.xy).a;
+                float distance3 = Sample2D(DiffMap, square.zw).a;
+                float distance4 = Sample2D(DiffMap, square.xw).a;
+                float distance5 = Sample2D(DiffMap, square.zy).a;
+
+                alpha += GetAlpha(distance2, width)
+                       + GetAlpha(distance3, width)
+                       + GetAlpha(distance4, width)
+                       + GetAlpha(distance5, width);
+            
+                // For calculating of average correct would be dividing by 5.
+                // But when text is blurred, its brightness is lost. Therefore divide by 4.
+                alpha = alpha * 0.25;
+            #endif
+
+            oColor.a = alpha;
+        }
 #else
-    oColor.a = iColor.a * Sample2D(DiffMap, iTexCoord).a;
+    #ifdef ALPHAMAP
+        oColor.rgb = iColor.rgb;
+        oColor.a = iColor.a * Sample2D(DiffMap, iTexCoord).a;
+    #else
+        oColor = iColor* Sample2D(DiffMap, iTexCoord);
+    #endif
 #endif
-}
+}

+ 9 - 2
Resources/CoreData/Shaders/HLSL/Transform.hlsl

@@ -106,7 +106,7 @@ float3 GetTrailNormal(float4 iPos, float3 iParentPos, float3 iForward)
 #endif
 
 #if defined(SKINNED)
-    #define iModelMatrix GetSkinMatrix(iBlendWeights, iBlendIndices);
+    #define iModelMatrix GetSkinMatrix(iBlendWeights, iBlendIndices)
 #elif defined(INSTANCED)
     #define iModelMatrix iModelInstance
 #else
@@ -137,7 +137,14 @@ float3 GetTrailNormal(float4 iPos, float3 iParentPos, float3 iForward)
     #define GetWorldNormal(modelMatrix) normalize(mul(iNormal, (float3x3)modelMatrix))
 #endif
 
-#define GetWorldTangent(modelMatrix) normalize(mul(iTangent.xyz, (float3x3)modelMatrix))
+#if defined(BILLBOARD)
+    #define GetWorldTangent(modelMatrix) float4(normalize(mul(float3(1.0, 0.0, 0.0), cBillboardRot)), 1.0)
+#elif defined(DIRBILLBOARD)
+    #define GetWorldTangent(modelMatrix) float4(normalize(mul(float3(1.0, 0.0, 0.0), (float3x3)modelMatrix)), 1.0)
+#else
+    #define GetWorldTangent(modelMatrix) float4(normalize(mul(iTangent.xyz, (float3x3)modelMatrix)), iTangent.w)
+#endif
+
 #endif
 
 #ifdef COMPILEPS

+ 10 - 0
Resources/CoreData/Shaders/HLSL/Uniforms.hlsl

@@ -60,7 +60,11 @@ uniform float4 cMatSpecColor;
 #ifdef PBR
     uniform float cRoughness;
     uniform float cMetallic;
+    uniform float cLightRad;
+    uniform float cLightLength;
 #endif
+uniform float3 cZoneMin;
+uniform float3 cZoneMax;
 uniform float cNearClipPS;
 uniform float cFarClipPS;
 uniform float4 cShadowCubeAdjust;
@@ -163,6 +167,8 @@ cbuffer ZonePS : register(b2)
     float4 cAmbientColor;
     float4 cFogParams;
     float3 cFogColor;
+    float3 cZoneMin;
+    float3 cZoneMax;
 }
 
 cbuffer LightPS : register(b3)
@@ -178,6 +184,10 @@ cbuffer LightPS : register(b3)
     float4 cShadowSplits;
     float2 cVSMShadowParams;
     float4x4 cLightMatricesPS[4];
+    #ifdef PBR
+        float cLightRad;
+        float cLightLength;
+    #endif
 }
 
 #ifndef CUSTOM_MATERIAL_CBUFFER

+ 145 - 0
Resources/CoreData/Shaders/HLSL/UnlitParticle.hlsl

@@ -0,0 +1,145 @@
+#include "Uniforms.hlsl"
+#include "Samplers.hlsl"
+#include "Transform.hlsl"
+#include "ScreenPos.hlsl"
+#include "Fog.hlsl"
+
+#if defined(COMPILEPS) && defined(SOFTPARTICLES)
+#ifndef D3D11
+// D3D9 uniform
+uniform float cSoftParticleFadeScale;
+#else
+// D3D11 constant buffer
+cbuffer CustomPS : register(b6)
+{
+    float cSoftParticleFadeScale;
+}
+#endif
+#endif
+
+void VS(float4 iPos : POSITION,
+    #ifndef NOUV
+        float2 iTexCoord : TEXCOORD0,
+    #endif
+    #ifdef VERTEXCOLOR
+        float4 iColor : COLOR0,
+    #endif
+    #ifdef SKINNED
+        float4 iBlendWeights : BLENDWEIGHT,
+        int4 iBlendIndices : BLENDINDICES,
+    #endif
+    #ifdef INSTANCED
+        float4x3 iModelInstance : TEXCOORD4,
+    #endif
+    #if defined(BILLBOARD) || defined(DIRBILLBOARD)
+        float2 iSize : TEXCOORD1,
+    #endif
+    #if defined(DIRBILLBOARD) || defined(TRAILBONE)
+        float3 iNormal : NORMAL,
+    #endif
+    #if defined(TRAILFACECAM) || defined(TRAILBONE)
+        float4 iTangent : TANGENT,
+    #endif
+    out float2 oTexCoord : TEXCOORD0,
+    #ifdef SOFTPARTICLES
+        out float4 oScreenPos : TEXCOORD1,
+    #endif
+    out float4 oWorldPos : TEXCOORD2,
+    #ifdef VERTEXCOLOR
+        out float4 oColor : COLOR0,
+    #endif
+    #if defined(D3D11) && defined(CLIPPLANE)
+        out float oClip : SV_CLIPDISTANCE0,
+    #endif
+    out float4 oPos : OUTPOSITION)
+{
+    // Define a 0,0 UV coord if not expected from the vertex data
+    #ifdef NOUV
+    float2 iTexCoord = float2(0.0, 0.0);
+    #endif
+
+    float4x3 modelMatrix = iModelMatrix;
+    float3 worldPos = GetWorldPos(modelMatrix);
+    oPos = GetClipPos(worldPos);
+    oTexCoord = GetTexCoord(iTexCoord);
+    oWorldPos = float4(worldPos, GetDepth(oPos));
+
+    #if defined(D3D11) && defined(CLIPPLANE)
+        oClip = dot(oPos, cClipPlane);
+    #endif
+
+    #ifdef SOFTPARTICLES
+        oScreenPos = GetScreenPos(oPos);
+    #endif
+
+    #ifdef VERTEXCOLOR
+        oColor = iColor;
+    #endif
+}
+
+void PS(float2 iTexCoord : TEXCOORD0,
+    #ifdef SOFTPARTICLES
+        float4 iScreenPos: TEXCOORD1,
+    #endif
+    float4 iWorldPos: TEXCOORD2,
+    #ifdef VERTEXCOLOR
+        float4 iColor : COLOR0,
+    #endif
+    #if defined(D3D11) && defined(CLIPPLANE)
+        float iClip : SV_CLIPDISTANCE0,
+    #endif
+    out float4 oColor : OUTCOLOR0)
+{
+    // Get material diffuse albedo
+    #ifdef DIFFMAP
+        float4 diffColor = cMatDiffColor * Sample2D(DiffMap, iTexCoord);
+        #ifdef ALPHAMASK
+            if (diffColor.a < 0.5)
+                discard;
+        #endif
+    #else
+        float4 diffColor = cMatDiffColor;
+    #endif
+
+    #ifdef VERTEXCOLOR
+        diffColor *= iColor;
+    #endif
+
+    // Get fog factor
+    #ifdef HEIGHTFOG
+        float fogFactor = GetHeightFogFactor(iWorldPos.w, iWorldPos.y);
+    #else
+        float fogFactor = GetFogFactor(iWorldPos.w);
+    #endif
+    
+    // Soft particle fade
+    // In expand mode depth test should be off. In that case do manual alpha discard test first to reduce fill rate
+    #ifdef SOFTPARTICLES
+        #if defined(EXPAND) && !defined(ADDITIVE)
+            if (diffColor.a < 0.01)
+                discard;
+        #endif
+
+        float particleDepth = iWorldPos.w;
+        float depth = Sample2DProj(DepthBuffer, iScreenPos).r;
+        #ifdef HWDEPTH
+            depth = ReconstructDepth(depth);
+        #endif
+
+        #ifdef EXPAND
+            float diffZ = max(particleDepth - depth, 0.0) * (cFarClipPS - cNearClipPS);
+            float fade = saturate(diffZ * cSoftParticleFadeScale);
+        #else
+            float diffZ = (depth - particleDepth) * (cFarClipPS - cNearClipPS);
+            float fade = saturate(1.0 - diffZ * cSoftParticleFadeScale);
+        #endif
+
+        #ifndef ADDITIVE
+            diffColor.a = max(diffColor.a - fade, 0.0);
+        #else
+            diffColor.rgb = max(diffColor.rgb - fade, float3(0.0, 0.0, 0.0));
+        #endif
+    #endif
+
+    oColor = float4(GetFog(diffColor.rgb, fogFactor), diffColor.a);
+}

+ 6 - 6
Resources/CoreData/Shaders/HLSL/Vegetation.hlsl

@@ -39,7 +39,7 @@ void VS(float4 iPos : POSITION,
     #if defined(LIGHTMAP) || defined(AO)
         float2 iTexCoord2 : TEXCOORD1,
     #endif
-    #if defined(NORMALMAP) || defined(TRAILFACECAM) || defined(TRAILBONE)
+    #if (defined(NORMALMAP) || defined(TRAILFACECAM) || defined(TRAILBONE)) && !defined(BILLBOARD) && !defined(DIRBILLBOARD)
         float4 iTangent : TANGENT,
     #endif
     #ifdef SKINNED
@@ -95,7 +95,7 @@ void VS(float4 iPos : POSITION,
 
     float4x3 modelMatrix = iModelMatrix;
     float3 worldPos = GetWorldPos(modelMatrix);
-    float height = worldPos.y - cModel._m31;
+    float height = worldPos.y - modelMatrix._m31;
 
     float windStrength = max(height - cWindHeightPivot, 0.0) * cWindHeightFactor;
     float windPeriod = cElapsedTime * cWindPeriod + dot(worldPos.xz, cWindWorldSpacing);
@@ -115,10 +115,10 @@ void VS(float4 iPos : POSITION,
     #endif
 
     #ifdef NORMALMAP
-        float3 tangent = GetWorldTangent(modelMatrix);
-        float3 bitangent = cross(tangent, oNormal) * iTangent.w;
+        float4 tangent = GetWorldTangent(modelMatrix);
+        float3 bitangent = cross(tangent.xyz, oNormal) * tangent.w;
         oTexCoord = float4(GetTexCoord(iTexCoord), bitangent.xy);
-        oTangent = float4(tangent, bitangent.z);
+        oTangent = float4(tangent.xyz, bitangent.z);
     #else
         oTexCoord = GetTexCoord(iTexCoord);
     #endif
@@ -155,7 +155,7 @@ void VS(float4 iPos : POSITION,
             for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
                 oVertexLight += GetVertexLight(i, worldPos, oNormal) * cVertexLights[i * 3].rgb;
         #endif
-        
+
         oScreenPos = GetScreenPos(oPos);
 
         #ifdef ENVCUBEMAP

+ 5 - 0
Resources/CoreData/Techniques/DiffLitParticleAlphaSoft.xml

@@ -0,0 +1,5 @@
+<technique vs="LitParticle" ps="LitParticle" vsdefines="SOFTPARTICLES" psdefines="DIFFMAP SOFTPARTICLES" >
+    <pass name="alpha" depthwrite="false" blend="alpha" />
+    <pass name="litalpha" depthwrite="false" blend="addalpha" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" />
+</technique>

+ 5 - 0
Resources/CoreData/Techniques/DiffLitParticleAlphaSoftExpand.xml

@@ -0,0 +1,5 @@
+<technique vs="LitParticle" ps="LitParticle" vsdefines="SOFTPARTICLES" psdefines="DIFFMAP SOFTPARTICLES EXPAND" >
+    <pass name="alpha" depthwrite="false" depthtest="false" blend="alpha" />
+    <pass name="litalpha" depthwrite="false" depthtest="false" blend="addalpha" />
+    <pass name="shadow" vs="Shadow" ps="Shadow" />
+</technique>

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

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

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

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

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

@@ -0,0 +1,3 @@
+<technique vs="UnlitParticle" ps="UnlitParticle" vsdefines="VERTEXCOLOR SOFTPARTICLES" psdefines="DIFFMAP VERTEXCOLOR ADDITIVE SOFTPARTICLES EXPAND">
+    <pass name="alpha" depthwrite="false" depthtest="false" blend="add" />
+</technique>

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

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

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

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

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

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

+ 15 - 0
Script/AtomicNET/AtomicNET/Graphics/AnimatedModel.cs

@@ -7,6 +7,21 @@ namespace AtomicEngine
     public partial class AnimatedModel : StaticModel
     {
 
+        
+        new public Model Model
+        {
+            get
+            {
+                return GetModel();
+            }
+            set
+            {
+                // AnimatedModel overloads StaticModel::SetModel(Model* model) with AnimatedModel::SetModel(Model*, bool createBones = false)
+                // need to make sure we pick right overload, otherwise will call wrong method native side
+                SetModel(value, true);
+            }
+        }
+
         public Skeleton Skeleton
         {
             get

+ 1 - 1
Script/AtomicNET/AtomicNET/IO/File.cs

@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
 
 namespace AtomicEngine
 {
-    public partial class File : AObject, Deserializer, Serializer
+    public partial class File : AObject, AbstractFile
     {
         private class Stream : System.IO.Stream
         {

+ 14 - 0
Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/AtomicActivity.java

@@ -20,6 +20,20 @@ public class AtomicActivity extends Activity {
         setContentView(mLayout);
     }
 
+    // ATOMIC BEGIN
+    /**
+     * This method is called by SDL if SDL did not handle a message itself.
+     * This happens if a received message contains an unsupported command.
+     * Method can be overwritten to handle Messages in a different class.
+     * @param command the command of the message.
+     * @param param the parameter of the message. May be null.
+     * @return if the message was handled in overridden method.
+     */
+    protected boolean onUnhandledMessage(int command, Object param) {
+        return false;
+    }
+    //ATOMIC END
+
     @Override
     protected void onResume() {
         super.onResume();

+ 298 - 95
Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/SDLActivity.java

@@ -21,7 +21,7 @@ import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
-import android.widget.AbsoluteLayout;
+import android.widget.RelativeLayout;
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -37,7 +37,9 @@ import android.content.pm.ActivityInfo;
 /**
  SDL Activity
  */
+// ATOMIC BEGIN
 public class SDLActivity {
+// ATOMIC END
     private static final String TAG = "SDL";
 
     // Keep track of the paused state
@@ -49,7 +51,9 @@ public class SDLActivity {
     public static boolean mSeparateMouseAndTouch;
 
     // Main components
+    // ATOMIC BEGIN
     protected static Activity mSingleton;
+    // ATOMIC END
     protected static SDLSurface mSurface;
     protected static View mTextEdit;
     protected static ViewGroup mLayout;
@@ -60,10 +64,35 @@ public class SDLActivity {
 
     // Audio
     protected static AudioTrack mAudioTrack;
+    protected static AudioRecord mAudioRecord;
 
     // Urho3D: flag to load the .so and a new method load them
     private static boolean mIsSharedLibraryLoaded = false;
 
+    // ATOMIC BEGIN
+    public static SDLSurface createSurface(Activity activity) {
+            SDLActivity.initialize();
+            mSingleton = activity;
+
+            if (!mIsSharedLibraryLoaded) {
+                System.loadLibrary("AtomicNETNative");
+                mIsSharedLibraryLoaded = true;
+            }
+            // Set up the surface
+            mSurface = new SDLSurface(activity.getApplication());
+
+            if(Build.VERSION.SDK_INT >= 12) {
+                mJoystickHandler = new SDLJoystickHandler_API12();
+            }
+            else {
+                mJoystickHandler = new SDLJoystickHandler();
+            }
+
+            return mSurface;
+        }
+    // ATOMIC END
+
+
     protected boolean onLoadLibrary(ArrayList<String> libraryNames) {
         for (final String name : libraryNames) {
             System.loadLibrary(name);
@@ -71,6 +100,17 @@ public class SDLActivity {
         return true;
     }
 
+    /**
+     * This method is called by SDL before starting the native application thread.
+     * It can be overridden to provide the arguments after the application name.
+     * The default implementation returns an empty array. It never returns null.
+     * @return arguments for the native application.
+     */
+    static protected String[] getArguments() {
+        // Urho3D: always return the "app_process" as the first argument instead of empty array
+        return new String[]{"app_process"};
+    }
+
     public static void initialize() {
         // The static nature of the singleton and Android quirkyness force us to initialize everything here
         // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
@@ -81,22 +121,93 @@ public class SDLActivity {
         mJoystickHandler = null;
         mSDLThread = null;
         mAudioTrack = null;
+        mAudioRecord = null;
         mExitCalledFromJava = false;
         mIsPaused = false;
         mIsSurfaceReady = false;
         mHasFocus = true;
     }
 
-    public static SDLSurface createSurface(Activity activity) {
+    // ATOMIC BEGIN
+    /*
+    // Setup
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log.v(TAG, "Device: " + android.os.Build.DEVICE);
+        Log.v(TAG, "Model: " + android.os.Build.MODEL);
+        Log.v(TAG, "onCreate(): " + mSingleton);
+        super.onCreate(savedInstanceState);
+
         SDLActivity.initialize();
-        mSingleton = activity;
+        // So we can call stuff from static callbacks
+        mSingleton = this;
 
+        // Urho3D: auto load all the shared libraries available in the library path
         if (!mIsSharedLibraryLoaded) {
-            System.loadLibrary("AtomicNETNative");
-            mIsSharedLibraryLoaded = true;
+            String libraryPath = getApplicationInfo().nativeLibraryDir;
+            File[] files = new File(libraryPath).listFiles(new FilenameFilter() {
+                @Override
+                public boolean accept(File dir, String filename) {
+                    // Only list libraries, i.e. exclude gdbserver when it presents
+                    // ATOMIC BEGIN
+                    // Do not load any file as a library that contains the word gdbserver, ever!
+                    if ( filename.contains("gdbserver")) {
+                        return false;
+                    }
+                    // ATOMIC END
+                    return filename.matches("^lib.*\\.so$");
+                }
+            });
+            Arrays.sort(files, new Comparator<File>() {
+                @Override
+                public int compare(File lhs, File rhs) {
+                    return Long.valueOf(lhs.lastModified()).compareTo(rhs.lastModified());
+                }
+            });
+            ArrayList<String> libraryNames = new ArrayList<String>(files.length);
+            for (final File libraryFilename : files) {
+                String name = libraryFilename.getName().replaceAll("^lib(.*)\\.so$", "$1");
+                libraryNames.add(name);
+            }
+
+            // Load shared libraries
+            String errorMsgBrokenLib = "";
+            try {
+                if (onLoadLibrary(libraryNames))
+                    mIsSharedLibraryLoaded = true;
+            } catch(UnsatisfiedLinkError e) {
+                errorMsgBrokenLib = e.getMessage();
+            } catch(Exception e) {
+                errorMsgBrokenLib = e.getMessage();
+            }
+
+            if (!errorMsgBrokenLib.isEmpty())
+            {
+                AlertDialog.Builder dlgAlert  = new AlertDialog.Builder(this);
+                dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
+                      + System.getProperty("line.separator")
+                      + System.getProperty("line.separator")
+                      + "Error: " + errorMsgBrokenLib);
+                dlgAlert.setTitle("SDL Error");
+                dlgAlert.setPositiveButton("Exit",
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog,int id) {
+                            // if this button is clicked, close current activity
+                            SDLActivity.mSingleton.finish();
+                        }
+                    });
+               dlgAlert.setCancelable(false);
+               dlgAlert.create().show();
+
+               return;
+            }
         }
+
         // Set up the surface
+        // ATOMIC BEGIN
         mSurface = new SDLSurface(activity.getApplication());
+        // ATOMIC END
 
         if(Build.VERSION.SDK_INT >= 12) {
             mJoystickHandler = new SDLJoystickHandler_API12();
@@ -105,15 +216,33 @@ public class SDLActivity {
             mJoystickHandler = new SDLJoystickHandler();
         }
 
-        return mSurface;
-    }
+        mLayout = new RelativeLayout(this);
+        mLayout.addView(mSurface);
+
+        setContentView(mLayout);
+
+        // Get filename from "Open with" of another application
+        Intent intent = getIntent();
+
+        if (intent != null && intent.getData() != null) {
+            String filename = intent.getData().getPath();
+            if (filename != null) {
+                Log.v(TAG, "Got filename: " + filename);
+                SDLActivity.onNativeDropFile(filename);
+            }
+        }
+    } */
 
     // Events
+
+    // ATOMIC SDLActivity activity methods changes to static, so can be called by C# activity
+
     public static void onPause() {
+
         Log.v(TAG, "onPause()");
 
         if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
-            return;
+           return;
         }
 
         SDLActivity.handlePause();
@@ -123,7 +252,7 @@ public class SDLActivity {
         Log.v(TAG, "onResume()");
 
         if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
-            return;
+           return;
         }
 
         SDLActivity.handleResume();
@@ -131,10 +260,11 @@ public class SDLActivity {
 
 
     public static void onWindowFocusChanged(boolean hasFocus) {
+
         Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
 
         if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
-            return;
+           return;
         }
 
         SDLActivity.mHasFocus = hasFocus;
@@ -147,7 +277,7 @@ public class SDLActivity {
         Log.v(TAG, "onLowMemory()");
 
         if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
-            return;
+           return;
         }
 
         SDLActivity.nativeLowMemory();
@@ -157,9 +287,9 @@ public class SDLActivity {
         Log.v(TAG, "onDestroy()");
 
         if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
-            // Reset everything in case the user re opens the app
-            SDLActivity.initialize();
-            return;
+           // Reset everything in case the user re opens the app
+           SDLActivity.initialize();
+           return;
         }
 
         // Send a quit message to the application
@@ -185,19 +315,19 @@ public class SDLActivity {
     public static boolean dispatchKeyEvent(KeyEvent event) {
 
         if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
-            return false;
+           return false;
         }
 
         int keyCode = event.getKeyCode();
         // Ignore certain special keys so they're handled by Android
         // Urho3D: also ignore the Home key
         if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
-                keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
-                keyCode == KeyEvent.KEYCODE_HOME ||
-                keyCode == KeyEvent.KEYCODE_CAMERA ||
-                keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */
-                keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */
-                ) {
+            keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
+            keyCode == KeyEvent.KEYCODE_HOME ||
+            keyCode == KeyEvent.KEYCODE_CAMERA ||
+            keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */
+            keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */
+            ) {
             return false;
         }
         return true;
@@ -226,16 +356,22 @@ public class SDLActivity {
             mSurface.handleResume();
         }
     }
+
+    // ATOMIC BEGIN
+    public static boolean FinishActivityOnNativeExit = true;
+    // ATOMIC END
+
     /* The native thread has finished */
-    public static void handleNativeExit() throws Exception {
+    public static void handleNativeExit() {
         SDLActivity.mSDLThread = null;
-        Log.d(TAG, "handleNativeExit");
-        if (mSingleton != null && FinishActivityOnNativeExit){
+
+        // ATOMIC BEGIN
+        if (mSingleton != null && FinishActivityOnNativeExit) {
             mSingleton.finish();
         }
+        // ATOMIC END
     }
 
-    public static boolean FinishActivityOnNativeExit = true;
 
     // Messages from the SDLMain thread
     static final int COMMAND_CHANGE_TITLE = 1;
@@ -271,41 +407,49 @@ public class SDLActivity {
                 return;
             }
             switch (msg.arg1) {
-                case COMMAND_CHANGE_TITLE:
-                    if (context instanceof Activity) {
-                        ((Activity) context).setTitle((String)msg.obj);
+            case COMMAND_CHANGE_TITLE:
+                if (context instanceof Activity) {
+                    ((Activity) context).setTitle((String)msg.obj);
+                } else {
+                    Log.e(TAG, "error handling message, getContext() returned no Activity");
+                }
+                break;
+            case COMMAND_TEXTEDIT_HIDE:
+                if (mTextEdit != null) {
+                    // Note: On some devices setting view to GONE creates a flicker in landscape.
+                    // Setting the View's sizes to 0 is similar to GONE but without the flicker.
+                    // The sizes will be set to useful values when the keyboard is shown again.
+                    mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
+
+                    InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+                    imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
+                }
+                break;
+            case COMMAND_SET_KEEP_SCREEN_ON:
+            {
+                Window window = ((Activity) context).getWindow();
+                if (window != null) {
+                    if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
+                        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                     } else {
-                        Log.e(TAG, "error handling message, getContext() returned no Activity");
-                    }
-                    break;
-                case COMMAND_TEXTEDIT_HIDE:
-                    if (mTextEdit != null) {
-                        mTextEdit.setVisibility(View.GONE);
-
-                        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
-                        imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
-                    }
-                    break;
-                case COMMAND_SET_KEEP_SCREEN_ON:
-                {
-                    Window window = ((Activity) context).getWindow();
-                    if (window != null) {
-                        if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
-                            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-                        } else {
-                            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-                        }
+                        window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                     }
-                    break;
                 }
-                default:
+                break;
+            }
+            default:
+                if ((context instanceof AtomicActivity) && !((AtomicActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) {
                     Log.e(TAG, "error handling message, command is " + msg.arg1);
+                }
             }
         }
     }
 
     // Handler for the messages
+    // ATOMIC BEGIN
+    // Made static
     static Handler commandHandler = new SDLCommandHandler();
+    // ATOMIC END
 
     // Send a message from the SDLMain thread
     public static boolean sendCommand(int command, Object data) {
@@ -416,17 +560,14 @@ public class SDLActivity {
 
         @Override
         public void run() {
-            AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(
-                    w, h + HEIGHT_PADDING, x, y);
+            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
+            params.leftMargin = x;
+            params.topMargin = y;
 
             if (mTextEdit == null) {
-                ViewParent parent = mSurface.getParent();
-                if (parent instanceof ViewGroup){
-                    mTextEdit = new DummyEdit(getContext());
-                    ((ViewGroup)parent).addView(mTextEdit, params);
-                } else {
-                    return;
-                }
+                mTextEdit = new DummyEdit(getContext());
+
+                mLayout.addView(mTextEdit, params);
             } else {
                 mTextEdit.setLayoutParams(params);
             }
@@ -459,7 +600,7 @@ public class SDLActivity {
     /**
      * This method is called by SDL using JNI.
      */
-    public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+    public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
         int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
         int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
         int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
@@ -538,13 +679,72 @@ public class SDLActivity {
     /**
      * This method is called by SDL using JNI.
      */
-    public static void audioQuit() {
+    public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
+        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
+        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+
+        Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+        // Let the user pick a larger buffer if they really want -- but ye
+        // gods they probably shouldn't, the minimums are horrifyingly high
+        // latency already
+        desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
+
+        if (mAudioRecord == null) {
+            mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
+                    channelConfig, audioFormat, desiredFrames * frameSize);
+
+            // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
+            if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+                Log.e(TAG, "Failed during initialization of AudioRecord");
+                mAudioRecord.release();
+                mAudioRecord = null;
+                return -1;
+            }
+
+            mAudioRecord.startRecording();
+        }
+
+        Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+        return 0;
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
+        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
+        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+        return mAudioRecord.read(buffer, 0, buffer.length);
+    }
+
+    /** This method is called by SDL using JNI. */
+    public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
+        // !!! FIXME: this is available in API Level 23. Until then, we always block.  :(
+        //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+        return mAudioRecord.read(buffer, 0, buffer.length);
+    }
+
+
+    /** This method is called by SDL using JNI. */
+    public static void audioClose() {
         if (mAudioTrack != null) {
             mAudioTrack.stop();
+            mAudioTrack.release();
             mAudioTrack = null;
         }
     }
 
+    /** This method is called by SDL using JNI. */
+    public static void captureClose() {
+        if (mAudioRecord != null) {
+            mAudioRecord.stop();
+            mAudioRecord.release();
+            mAudioRecord = null;
+        }
+    }
+
+
     // Input
 
     /**
@@ -579,29 +779,35 @@ public class SDLActivity {
         }
     }
 
+    // Check if a given device is considered a possible SDL joystick
+    public static boolean isDeviceSDLJoystick(int deviceId) {
+        InputDevice device = InputDevice.getDevice(deviceId);
+        // We cannot use InputDevice.isVirtual before API 16, so let's accept
+        // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
+        if ((device == null) || (deviceId < 0)) {
+            return false;
+        }
+        int sources = device.getSources();
+        return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
+                ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
+                ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
+        );
+    }
+
     // APK expansion files support
 
     /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
-    private static Object expansionFile;
+    private Object expansionFile;
 
     /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
-    private static Method expansionFileMethod;
-
-    /**
-     * This method was called by SDL using JNI.
-     * @deprecated because of an incorrect name
-     */
-    @Deprecated
-    public InputStream openAPKExtensionInputStream(String fileName) throws IOException {
-        return openAPKExpansionInputStream(fileName);
-    }
+    private Method expansionFileMethod;
 
     /**
      * This method is called by SDL using JNI.
      * @return an InputStream on success or null if no expansion file was used.
      * @throws IOException on errors. Message is set for the SDL error message.
      */
-    public static InputStream openAPKExpansionInputStream(String fileName) throws IOException {
+    public InputStream openAPKExpansionInputStream(String fileName) throws IOException {
         // Get a ZipResourceFile representing a merger of both the main and patch files
         if (expansionFile == null) {
             String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION");
@@ -627,11 +833,11 @@ public class SDLActivity {
                 // To avoid direct dependency on Google APK expansion library that is
                 // not a part of Android SDK we access it using reflection
                 expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
-                        .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
-                        .invoke(null, mSingleton, mainVersion, patchVersion);
+                    .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
+                    .invoke(null, this, mainVersion, patchVersion);
 
                 expansionFileMethod = expansionFile.getClass()
-                        .getMethod("getInputStream", String.class);
+                    .getMethod("getInputStream", String.class);
             } catch (Exception ex) {
                 ex.printStackTrace();
                 expansionFile = null;
@@ -661,7 +867,7 @@ public class SDLActivity {
     // Messagebox
 
     /** Result of current messagebox. Also used for blocking the calling thread. */
-    protected final int[] messageboxSelection = new int[1];
+    static protected final int[] messageboxSelection = new int[1];
 
     /** Id of current dialog. */
     protected int dialogs = 0;
@@ -729,7 +935,7 @@ public class SDLActivity {
         return messageboxSelection[0];
     }
 
-    protected Dialog onCreateDialog(int ignore, Bundle args) {
+    public static Dialog onCreateDialog(int ignore, Bundle args) {
 
         // TODO set values from "flags" to messagebox dialog
 
@@ -864,16 +1070,16 @@ public class SDLActivity {
 }
 
 /**
- Simple nativeInit() runnable
- */
+    Simple nativeInit() runnable
+*/
 class SDLMain implements Runnable {
     @Override
     public void run() {
         // Runs SDL_main()
         // Urho3D: pass filesDir
-        String path = ((Activity)SDLActivity.getContext()).getFilesDir().getAbsolutePath();
-        Log.v("SDLMain", "Init with path: " + path);
-        SDLActivity.nativeInit(new String[]{"app_process"}, path);
+        SDLActivity.nativeInit(SDLActivity.getArguments(), ((Activity)SDLActivity.getContext()).getFilesDir().getAbsolutePath());
+
+        //Log.v("SDL", "SDL thread terminated");
     }
 }
 
@@ -917,7 +1123,7 @@ class DummyEdit extends View implements View.OnKeyListener {
         // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
         // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
         // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
-        // FIXME: A more effective solution would be to change our Layout from AbsoluteLayout to Relative or Linear
+        // FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
         // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
         // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
         if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
@@ -956,7 +1162,7 @@ class SDLInputConnection extends BaseInputConnection {
          */
         int keyCode = event.getKeyCode();
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            if (event.isPrintingKey()) {
+            if (event.isPrintingKey() || keyCode == KeyEvent.KEYCODE_SPACE) {
                 commitText(String.valueOf((char) event.getUnicodeChar()), 1);
             }
             SDLActivity.onNativeKeyDown(keyCode);
@@ -995,7 +1201,7 @@ class SDLInputConnection extends BaseInputConnection {
         if (beforeLength == 1 && afterLength == 0) {
             // backspace
             return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
-                    && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
+                && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
         }
 
         return super.deleteSurroundingText(beforeLength, afterLength);
@@ -1057,9 +1263,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
             if (joystick == null) {
                 joystick = new SDLJoystick();
                 InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
-
-                // Urho3D - revert back commit 34a0b0478654e8dfaf111aecc0e4535875c4ec87 as it does more harm than good
-                if( (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+                if (SDLActivity.isDeviceSDLJoystick(deviceIds[i])) {
                     joystick.device_id = deviceIds[i];
                     joystick.name = joystickDevice.getName();
                     joystick.axes = new ArrayList<InputDevice.MotionRange>();
@@ -1068,9 +1272,9 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
                     List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
                     Collections.sort(ranges, new RangeComparator());
                     for (InputDevice.MotionRange range : ranges ) {
-                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) {
+                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
                             if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
-                                    range.getAxis() == MotionEvent.AXIS_HAT_Y) {
+                                range.getAxis() == MotionEvent.AXIS_HAT_Y) {
                                 joystick.hats.add(range);
                             }
                             else {
@@ -1081,7 +1285,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
 
                     mJoysticks.add(joystick);
                     SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, 0, -1,
-                            joystick.axes.size(), joystick.hats.size()/2, 0);
+                                                  joystick.axes.size(), joystick.hats.size()/2, 0);
                 }
             }
         }
@@ -1122,7 +1326,7 @@ class SDLJoystickHandler_API12 extends SDLJoystickHandler {
 
     @Override
     public boolean handleMotionEvent(MotionEvent event) {
-        if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
+        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
             int actionPointerIndex = event.getActionIndex();
             int action = event.getActionMasked();
             switch(action) {
@@ -1161,8 +1365,7 @@ class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
             case InputDevice.SOURCE_JOYSTICK:
             case InputDevice.SOURCE_GAMEPAD:
             case InputDevice.SOURCE_DPAD:
-                SDLActivity.handleJoystickMotionEvent(event);
-                return true;
+                return SDLActivity.handleJoystickMotionEvent(event);
 
             case InputDevice.SOURCE_MOUSE:
                 action = event.getActionMasked();

+ 87 - 74
Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/SDLSurface.java

@@ -20,13 +20,13 @@ import android.view.View;
 import android.view.WindowManager;
 
 /**
- SDLSurface. This is what we draw on, so we need to know when it's created
- in order to do anything useful.
+    SDLSurface. This is what we draw on, so we need to know when it's created
+    in order to do anything useful.
 
- Because of this, that's where we set up the SDL thread
- */
+    Because of this, that's where we set up the SDL thread
+*/
 public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
-        View.OnKeyListener, View.OnTouchListener, SensorEventListener {
+    View.OnKeyListener, View.OnTouchListener, SensorEventListener  {
 
     // Sensors
     protected static SensorManager mSensorManager;
@@ -100,47 +100,47 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
 
         int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
         switch (format) {
-            case PixelFormat.A_8:
-                Log.v("SDL", "pixel format A_8");
-                break;
-            case PixelFormat.LA_88:
-                Log.v("SDL", "pixel format LA_88");
-                break;
-            case PixelFormat.L_8:
-                Log.v("SDL", "pixel format L_8");
-                break;
-            case PixelFormat.RGBA_4444:
-                Log.v("SDL", "pixel format RGBA_4444");
-                sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
-                break;
-            case PixelFormat.RGBA_5551:
-                Log.v("SDL", "pixel format RGBA_5551");
-                sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
-                break;
-            case PixelFormat.RGBA_8888:
-                Log.v("SDL", "pixel format RGBA_8888");
-                sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
-                break;
-            case PixelFormat.RGBX_8888:
-                Log.v("SDL", "pixel format RGBX_8888");
-                sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
-                break;
-            case PixelFormat.RGB_332:
-                Log.v("SDL", "pixel format RGB_332");
-                sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
-                break;
-            case PixelFormat.RGB_565:
-                Log.v("SDL", "pixel format RGB_565");
-                sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
-                break;
-            case PixelFormat.RGB_888:
-                Log.v("SDL", "pixel format RGB_888");
-                // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
-                sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
-                break;
-            default:
-                Log.v("SDL", "pixel format unknown " + format);
-                break;
+        case PixelFormat.A_8:
+            Log.v("SDL", "pixel format A_8");
+            break;
+        case PixelFormat.LA_88:
+            Log.v("SDL", "pixel format LA_88");
+            break;
+        case PixelFormat.L_8:
+            Log.v("SDL", "pixel format L_8");
+            break;
+        case PixelFormat.RGBA_4444:
+            Log.v("SDL", "pixel format RGBA_4444");
+            sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
+            break;
+        case PixelFormat.RGBA_5551:
+            Log.v("SDL", "pixel format RGBA_5551");
+            sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
+            break;
+        case PixelFormat.RGBA_8888:
+            Log.v("SDL", "pixel format RGBA_8888");
+            sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
+            break;
+        case PixelFormat.RGBX_8888:
+            Log.v("SDL", "pixel format RGBX_8888");
+            sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
+            break;
+        case PixelFormat.RGB_332:
+            Log.v("SDL", "pixel format RGB_332");
+            sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
+            break;
+        case PixelFormat.RGB_565:
+            Log.v("SDL", "pixel format RGB_565");
+            sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
+            break;
+        case PixelFormat.RGB_888:
+            Log.v("SDL", "pixel format RGB_888");
+            // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
+            sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
+            break;
+        default:
+            Log.v("SDL", "pixel format unknown " + format);
+            break;
         }
 
         mWidth = width;
@@ -159,28 +159,28 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
         else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
         {
             if (mWidth > mHeight) {
-                skip = true;
+               skip = true;
             }
         } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
             if (mWidth < mHeight) {
-                skip = true;
+               skip = true;
             }
         }
 
         // Special Patch for Square Resolution: Black Berry Passport
         if (skip) {
-            double min = Math.min(mWidth, mHeight);
-            double max = Math.max(mWidth, mHeight);
+           double min = Math.min(mWidth, mHeight);
+           double max = Math.max(mWidth, mHeight);
 
-            if (max / min < 1.20) {
-                Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
-                skip = false;
-            }
+           if (max / min < 1.20) {
+              Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
+              skip = false;
+           }
         }
 
         if (skip) {
-            Log.v("SDL", "Skip .. Surface is not ready.");
-            return;
+           Log.v("SDL", "Skip .. Surface is not ready.");
+           return;
         }
 
 
@@ -208,11 +208,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
                     finally{
                         // Native thread has finished
                         if (! SDLActivity.mExitCalledFromJava) {
-                            try {
-                                SDLActivity.handleNativeExit();
-                            } catch (Exception e) {
-                                e.printStackTrace();
-                            }
+                            SDLActivity.handleNativeExit();
                         }
                     }
                 }
@@ -229,11 +225,14 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
     @Override
     public boolean onKey(View  v, int keyCode, KeyEvent event) {
         // Dispatch the different events depending on where they come from
-        // Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
-        // So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as KEYBOARD
-
-        if ( (event.getSource() & InputDevice.SOURCE_GAMEPAD) != 0 ||
-                (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) {
+        // Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
+        // So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
+        //
+        // Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
+        // SOURCE_JOYSTICK, while its key events arrive from the keyboard source
+        // So, retrieve the device itself and check all of its sources
+        if (SDLActivity.isDeviceSDLJoystick(event.getDeviceId())) {
+            // Note that we process events with specific key codes here
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
                     return true;
@@ -245,7 +244,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
             }
         }
 
-        if( (event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
+        if ((event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 //Log.v("SDL", "key down: " + keyCode);
                 SDLActivity.onNativeKeyDown(keyCode);
@@ -258,6 +257,20 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
             }
         }
 
+        if ((event.getSource() & InputDevice.SOURCE_MOUSE) != 0) {
+            // on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
+            // they are ignored here because sending them as mouse input to SDL is messy
+            if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
+                switch (event.getAction()) {
+                case KeyEvent.ACTION_DOWN:
+                case KeyEvent.ACTION_UP:
+                    // mark the event as handled or it will be handled by system
+                    // handling KEYCODE_BACK by system will call onBackPressed()
+                    return true;
+                }
+            }
+        }
+
         return false;
     }
 
@@ -276,7 +289,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
         // !!! FIXME: dump this SDK check after 2.0.4 ships and require API14.
         if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) {
             if (Build.VERSION.SDK_INT < 14) {
-                mouseButton = 1;    // For Android==12 all mouse buttons are the left button
+                mouseButton = 1; // all mouse buttons are the left button
             } else {
                 try {
                     mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
@@ -346,18 +359,18 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
         }
 
         return true;
-    }
+   }
 
     // Sensor events
     public void enableSensor(int sensortype, boolean enabled) {
         // TODO: This uses getDefaultSensor - what if we have >1 accels?
         if (enabled) {
             mSensorManager.registerListener(this,
-                    mSensorManager.getDefaultSensor(sensortype),
-                    SensorManager.SENSOR_DELAY_GAME, null);
+                            mSensorManager.getDefaultSensor(sensortype),
+                            SensorManager.SENSOR_DELAY_GAME, null);
         } else {
             mSensorManager.unregisterListener(this,
-                    mSensorManager.getDefaultSensor(sensortype));
+                            mSensorManager.getDefaultSensor(sensortype));
         }
     }
 
@@ -390,8 +403,8 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
                     break;
             }
             SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
-                    y / SensorManager.GRAVITY_EARTH,
-                    event.values[2] / SensorManager.GRAVITY_EARTH - 1);
+                                      y / SensorManager.GRAVITY_EARTH,
+                                      event.values[2] / SensorManager.GRAVITY_EARTH);
         }
     }
 }

BIN
Script/AtomicNET/Platform/Android/JavaSDLBin/atomicjavasdl-release.aar


+ 3 - 1
Script/Packages/Atomic/Core.json

@@ -10,7 +10,9 @@
 			"SendEvent" : ["StringHash"]
 		},
 		"Context" : {
-			"GetTypeName" : ["StringHash"]
+			"GetTypeName" : ["StringHash"],
+			"RequireIK" : [],
+			"ReleaseIK" : []
 		},
 		"CSharp" : {
 			"Object" : {

+ 1 - 1
Script/Packages/Atomic/IO.json

@@ -2,7 +2,7 @@
 	"name" : "IO",
 	"sources" : ["Source/Atomic/IO"],
 	"classes" : ["Log", "File", "FileSystem", "FileWatcher", "BufferQueue", "PackageFile"],
-	"interfaces" : ["Serializer", "Deserializer"],
+	"interfaces" : ["Serializer", "Deserializer", "AbstractFile"],
 	"overloads" : {
 		"File" : {
 			"File" : ["Context", "String", "FileMode"]

+ 1 - 1
Script/Packages/Atomic/Scene.json

@@ -15,7 +15,7 @@
 	"overloads" : {
 
 		"Node" : {
-			"CreateChild" : ["String", "CreateMode", "unsigned"],
+			"CreateChild" : ["String", "CreateMode", "unsigned", "bool"],
 			"GetChild" : ["String", "bool"],
 			"SetScale" : ["Vector3"],
 			"SetPosition2D" : ["Vector2"],

+ 3 - 8
Source/Atomic/Atomic2D/AnimatedSprite2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -59,7 +59,6 @@ AnimatedSprite2D::AnimatedSprite2D(Context* context) :
     animationStateData_(0),
     animationState_(0),
 #endif
-    spriterInstance_(0),
     speed_(1.0f),
     loopMode_(LM_DEFAULT)
 {
@@ -452,7 +451,7 @@ void AnimatedSprite2D::UpdateSourceBatchesSpriter()
     Vertex2D vertex3;
 
     const PODVector<Spriter::SpatialTimelineKey*>& timelineKeys = spriterInstance_->GetTimelineKeys();
-    for (size_t i = 0; i < timelineKeys.Size(); ++i)
+    for (unsigned i = 0; i < timelineKeys.Size(); ++i)
     {
         if (timelineKeys[i]->GetObjectType() != Spriter::SPRITE)
             continue;
@@ -527,11 +526,7 @@ void AnimatedSprite2D::Dispose()
         skeleton_ = 0;
     }
 #endif
-    if (spriterInstance_)
-    {
-        delete spriterInstance_;
-        spriterInstance_ = 0;
-    }
+    spriterInstance_.Reset();
 }
 
 }

+ 2 - 2
Source/Atomic/Atomic2D/AnimatedSprite2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -143,7 +143,7 @@ protected:
 #endif
     
     /// Spriter instance.
-    Spriter::SpriterInstance* spriterInstance_;
+    UniquePtr<Spriter::SpriterInstance> spriterInstance_;
 };
 
 }

+ 7 - 12
Source/Atomic/Atomic2D/AnimationSet2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -110,7 +110,6 @@ AnimationSet2D::AnimationSet2D(Context* context) :
     skeletonData_(0),
     atlas_(0),
 #endif
-    spriterData_(0),
     hasSpriteSheet_(false)
 {
 }
@@ -194,11 +193,11 @@ bool AnimationSet2D::HasAnimation(const String& animationName) const
                 return true;
         }
     }
-#endif    
+#endif
     if (spriterData_ && !spriterData_->entities_.Empty())
     {
         const PODVector<Spriter::Animation*>& animations = spriterData_->entities_[0]->animations_;
-        for (size_t i = 0; i < animations.Size(); ++i)
+        for (unsigned i = 0; i < animations.Size(); ++i)
         {
             if (animationName == animations[i]->name_)
                 return true;
@@ -259,7 +258,7 @@ bool AnimationSet2D::EndLoadSpine()
 
     if (numAtlasPages > 1)
     {
-        ATOMIC_LOGERROR("Only one page is supported in Urho3D");
+        ATOMIC_LOGERROR("Only one page is supported in Atomic");
         return false;
     }
 
@@ -322,10 +321,10 @@ bool AnimationSet2D::BeginLoadSpriter(Deserializer& source)
             cache->BackgroundLoadResource<SpriteSheet2D>(spriteSheetFilePath_, true, this);
         else
         {
-            for (size_t i = 0; i < spriterData_->folders_.Size(); ++i)
+            for (unsigned i = 0; i < spriterData_->folders_.Size(); ++i)
             {
                 Spriter::Folder* folder = spriterData_->folders_[i];
-                for (size_t j = 0; j < folder->files_.Size(); ++j)
+                for (unsigned j = 0; j < folder->files_.Size(); ++j)
                 {
                     Spriter::File* file = folder->files_[j];
                     String imagePath = parentPath + file->name_;
@@ -525,11 +524,7 @@ void AnimationSet2D::Dispose()
     }
 #endif
 
-    if (spriterData_)
-    {
-        delete spriterData_;
-        spriterData_ = 0;
-    }
+    spriterData_.Reset();
 
     sprite_.Reset();
     spriteSheet_.Reset();

+ 3 - 3
Source/Atomic/Atomic2D/AnimationSet2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -76,7 +76,7 @@ public:
 #endif
 
     /// Return spriter data.
-    Spriter::SpriterData* GetSpriterData() const { return spriterData_; }
+    Spriter::SpriterData* GetSpriterData() const { return spriterData_.Get(); }
     /// Return spriter file sprite.
     Sprite2D* GetSpriterFileSprite(int folderId, int fileId) const;
 
@@ -109,7 +109,7 @@ private:
 #endif
     
     /// Spriter data.
-    Spriter::SpriterData* spriterData_;
+    UniquePtr<Spriter::SpriterData> spriterData_;
     /// Has sprite sheet.
     bool hasSpriteSheet_;
     /// Sprite sheet file path.

+ 1 - 1
Source/Atomic/Atomic2D/Atomic2D.cpp

@@ -1,4 +1,4 @@
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/Atomic2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 44 - 0
Source/Atomic/Atomic2D/Atomic2DEvents.h

@@ -0,0 +1,44 @@
+//
+// Copyright (c) 2008-2017 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Core/Object.h"
+
+namespace Atomic
+{
+
+/// Emitting ParticleEmitter2D particles stopped.
+ATOMIC_EVENT(E_PARTICLESEND, ParticlesEnd)
+{
+    ATOMIC_PARAM(P_NODE, Node);                    // Node pointer
+    ATOMIC_PARAM(P_EFFECT, Effect);                // ParticleEffect2D pointer
+}
+
+/// All ParticleEmitter2D particles have been removed.
+ATOMIC_EVENT(E_PARTICLESDURATION, ParticlesDuration)
+{
+    ATOMIC_PARAM(P_NODE, Node);                    // Node pointer
+    ATOMIC_PARAM(P_EFFECT, Effect);                // ParticleEffect2D pointer
+}
+
+}

+ 1 - 1
Source/Atomic/Atomic2D/CollisionBox2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionBox2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionChain2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionChain2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionCircle2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionCircle2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionEdge2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionEdge2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionPolygon2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/CollisionPolygon2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 9 - 1
Source/Atomic/Atomic2D/CollisionShape2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -223,7 +223,11 @@ void CollisionShape2D::CreateFixture()
     // Chain shape must have atleast two vertices before creating fixture
     if (fixtureDef_.shape->m_type != b2Shape::e_chain || static_cast<const b2ChainShape*>(fixtureDef_.shape)->m_count >= 2)
     {
+        b2MassData massData;
+        body->GetMassData(&massData);
         fixture_ = body->CreateFixture(&fixtureDef_);
+        if (!rigidBody_->GetUseFixtureMass()) // Workaround for resetting mass in CreateFixture().
+            body->SetMassData(&massData);
         fixture_->SetUserData(this);
     }
 }
@@ -240,7 +244,11 @@ void CollisionShape2D::ReleaseFixture()
     if (!body)
         return;
 
+    b2MassData massData;
+    body->GetMassData(&massData);
     body->DestroyFixture(fixture_);
+    if (!rigidBody_->GetUseFixtureMass()) // Workaround for resetting mass in DestroyFixture().
+        body->SetMassData(&massData);
     fixture_ = 0;
 }
 

+ 1 - 1
Source/Atomic/Atomic2D/CollisionShape2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 36 - 3
Source/Atomic/Atomic2D/Constraint2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -41,7 +41,8 @@ extern const char* ATOMIC2D_CATEGORY;
 Constraint2D::Constraint2D(Context* context) :
     Component(context),
     joint_(0),
-    collideConnected_(false)
+    collideConnected_(false),
+    otherBodyNodeIDDirty_(false)
 {
 
 }
@@ -54,6 +55,31 @@ Constraint2D::~Constraint2D()
 void Constraint2D::RegisterObject(Context* context)
 {
     ATOMIC_ACCESSOR_ATTRIBUTE("Collide Connected", GetCollideConnected, SetCollideConnected, bool, false, AM_DEFAULT);
+    ATOMIC_ATTRIBUTE("Other Body NodeID", unsigned, otherBodyNodeID_, 0, AM_DEFAULT | AM_NODEID);
+}
+
+void Constraint2D::OnSetAttribute(const AttributeInfo& attr, const Variant& src)
+{
+    Serializable::OnSetAttribute(attr, src);
+
+    if (!attr.accessor_ && attr.offset_ == offsetof(Constraint2D, otherBodyNodeID_))
+        otherBodyNodeIDDirty_ = true;
+}
+
+void Constraint2D::ApplyAttributes()
+{
+    // If other body node ID dirty, try to find it now and apply
+    if (otherBodyNodeIDDirty_)
+    {
+        Scene* scene = GetScene();
+        if (scene)
+        {
+            Node* otherNode = scene->GetNode(otherBodyNodeID_);
+            if (otherNode)
+                SetOtherBody(otherNode->GetComponent<RigidBody2D>());
+        }
+        otherBodyNodeIDDirty_ = false;
+    }
 }
 
 void Constraint2D::OnSetEnabled()
@@ -107,6 +133,9 @@ void Constraint2D::SetOtherBody(RigidBody2D* body)
 
     otherBody_ = body;
 
+    Node* otherNode = body ? body->GetNode() : (Node*)0;
+    otherBodyNodeID_ = otherNode ? otherNode->GetID() : 0;
+
     RecreateJoint();
     MarkNetworkUpdate();
 }
@@ -145,7 +174,11 @@ void Constraint2D::OnNodeSet(Node* node)
 void Constraint2D::OnSceneSet(Scene* scene)
 {
     if (scene)
-        physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld2D>();
+    {
+        physicsWorld_ = scene->GetDerivedComponent<PhysicsWorld2D>();
+        if (!physicsWorld_)
+            physicsWorld_ = scene->CreateComponent<PhysicsWorld2D>();
+    }
 }
 
 void Constraint2D::InitializeJointDef(b2JointDef* jointDef)

+ 10 - 2
Source/Atomic/Atomic2D/Constraint2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -45,6 +45,10 @@ public:
     /// Register object factory.
     static void RegisterObject(Context* context);
 
+    /// Handle attribute write access.
+    virtual void OnSetAttribute(const AttributeInfo& attr, const Variant& src);
+    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
+    virtual void ApplyAttributes();
     /// Handle enabled/disabled state change.
     virtual void OnSetEnabled();
     /// Create joint.
@@ -94,8 +98,12 @@ protected:
     WeakPtr<RigidBody2D> ownerBody_;
     /// Other body.
     WeakPtr<RigidBody2D> otherBody_;
-    /// Collide connected.
+    /// Other body node ID for serialization.
+    unsigned otherBodyNodeID_;
+    /// Collide connected flag.
     bool collideConnected_;
+    /// Other body node ID dirty flag.
+    bool otherBodyNodeIDDirty_;
     /// Attached constraint.
     WeakPtr<Constraint2D> attachedConstraint_;
 };

+ 27 - 3
Source/Atomic/Atomic2D/ConstraintDistance2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -55,6 +55,7 @@ void ConstraintDistance2D::RegisterObject(Context* context)
     ATOMIC_ACCESSOR_ATTRIBUTE("Other Body Anchor", GetOtherBodyAnchor, SetOtherBodyAnchor, Vector2, Vector2::ZERO, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Frequency Hz", GetFrequencyHz, SetFrequencyHz, float, 0.0f, AM_DEFAULT);
     ATOMIC_ACCESSOR_ATTRIBUTE("Damping Ratio", GetDampingRatio, SetDampingRatio, float, 0.0f, AM_DEFAULT);
+    ATOMIC_ACCESSOR_ATTRIBUTE("Length", GetLength, SetLength, float, 1.0f, AM_DEFAULT);
     ATOMIC_COPY_BASE_ATTRIBUTES(Constraint2D);
 }
 
@@ -87,7 +88,11 @@ void ConstraintDistance2D::SetFrequencyHz(float frequencyHz)
 
     jointDef_.frequencyHz = frequencyHz;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2DistanceJoint*>(joint_)->SetFrequency(frequencyHz);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -98,7 +103,26 @@ void ConstraintDistance2D::SetDampingRatio(float dampingRatio)
 
     jointDef_.dampingRatio = dampingRatio;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2DistanceJoint*>(joint_)->SetDampingRatio(dampingRatio);
+    else
+        RecreateJoint();
+
+    MarkNetworkUpdate();
+}
+
+void ConstraintDistance2D::SetLength(float length)
+{
+    if (length == jointDef_.length)
+        return;
+
+    jointDef_.length = length;
+
+    if (joint_)
+        static_cast<b2DistanceJoint*>(joint_)->SetLength(length);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 6 - 1
Source/Atomic/Atomic2D/ConstraintDistance2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -48,6 +48,8 @@ public:
     void SetFrequencyHz(float frequencyHz);
     /// Set damping ratio.
     void SetDampingRatio(float dampingRatio);
+    /// Set length.
+    void SetLength(float length);
 
     /// Return owner body anchor.
     const Vector2& GetOwnerBodyAnchor() const { return ownerBodyAnchor_; }
@@ -61,6 +63,9 @@ public:
     /// Return damping ratio.
     float GetDampingRatio() const { return jointDef_.dampingRatio; }
 
+    /// Return length.
+    float GetLength() const { return jointDef_.length; }
+
 private:
     /// Return joint def.
     virtual b2JointDef* GetJointDef();

+ 11 - 3
Source/Atomic/Atomic2D/ConstraintFriction2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -74,7 +74,11 @@ void ConstraintFriction2D::SetMaxForce(float maxForce)
 
     jointDef_.maxForce = maxForce;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2FrictionJoint*>(joint_)->SetMaxForce(maxForce);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -86,7 +90,11 @@ void ConstraintFriction2D::SetMaxTorque(float maxTorque)
 
     jointDef_.maxTorque = maxTorque;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2FrictionJoint*>(joint_)->SetMaxTorque(maxTorque);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintFriction2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 6 - 2
Source/Atomic/Atomic2D/ConstraintGear2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -94,7 +94,11 @@ void ConstraintGear2D::SetRatio(float ratio)
 
     jointDef_.ratio = ratio;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2GearJoint*>(joint_)->SetRatio(ratio);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintGear2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 26 - 6
Source/Atomic/Atomic2D/ConstraintMotor2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -65,7 +65,11 @@ void ConstraintMotor2D::SetLinearOffset(const Vector2& linearOffset)
 
     linearOffset_ = linearOffset;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2MotorJoint*>(joint_)->SetLinearOffset(ToB2Vec2(linearOffset));
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -76,7 +80,11 @@ void ConstraintMotor2D::SetAngularOffset(float angularOffset)
 
     jointDef_.angularOffset = angularOffset;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2MotorJoint*>(joint_)->SetAngularOffset(angularOffset);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -87,7 +95,11 @@ void ConstraintMotor2D::SetMaxForce(float maxForce)
 
     jointDef_.maxForce = maxForce;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2MotorJoint*>(joint_)->SetMaxForce(maxForce);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -98,7 +110,11 @@ void ConstraintMotor2D::SetMaxTorque(float maxTorque)
 
     jointDef_.maxTorque = maxTorque;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2MotorJoint*>(joint_)->SetMaxTorque(maxTorque);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -109,7 +125,11 @@ void ConstraintMotor2D::SetCorrectionFactor(float correctionFactor)
 
     jointDef_.correctionFactor = correctionFactor;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2MotorJoint*>(joint_)->SetCorrectionFactor(correctionFactor);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintMotor2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 21 - 16
Source/Atomic/Atomic2D/ConstraintMouse2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -36,8 +36,7 @@ extern const char* ATOMIC2D_CATEGORY;
 
 ConstraintMouse2D::ConstraintMouse2D(Context* context) :
     Constraint2D(context),
-    target_(Vector2::ZERO),
-    targetSetted_(false)
+    target_(Vector2::ZERO)
 {
 }
 
@@ -63,18 +62,12 @@ void ConstraintMouse2D::SetTarget(const Vector2& target)
         return;
 
     target_ = target;
-    if (joint_ && targetSetted_)
-    {
-        b2MouseJoint* mouseJoint = (b2MouseJoint*)joint_;
-        mouseJoint->SetTarget(ToB2Vec2(target_));
 
-        MarkNetworkUpdate();
-        return;
-    }
-
-    targetSetted_ = true;
+    if (joint_)
+        static_cast<b2MouseJoint*>(joint_)->SetTarget(ToB2Vec2(target));
+    else
+        RecreateJoint();
 
-    RecreateJoint();
     MarkNetworkUpdate();
 }
 
@@ -85,7 +78,11 @@ void ConstraintMouse2D::SetMaxForce(float maxForce)
 
     jointDef_.maxForce = maxForce;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2MouseJoint*>(joint_)->SetMaxForce(maxForce);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -96,7 +93,11 @@ void ConstraintMouse2D::SetFrequencyHz(float frequencyHz)
 
     jointDef_.frequencyHz = frequencyHz;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2MouseJoint*>(joint_)->SetFrequency(frequencyHz);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -107,7 +108,11 @@ void ConstraintMouse2D::SetDampingRatio(float dampingRatio)
 
     jointDef_.dampingRatio = dampingRatio;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2MouseJoint*>(joint_)->SetDampingRatio(dampingRatio);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 1 - 3
Source/Atomic/Atomic2D/ConstraintMouse2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -69,8 +69,6 @@ private:
     b2MouseJointDef jointDef_;
     /// Target.
     Vector2 target_;
-    /// Target setted.
-    bool targetSetted_;
 };
 
 }

+ 31 - 7
Source/Atomic/Atomic2D/ConstraintPrismatic2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -90,7 +90,11 @@ void ConstraintPrismatic2D::SetEnableLimit(bool enableLimit)
 
     jointDef_.enableLimit = enableLimit;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2PrismaticJoint*>(joint_)->EnableLimit(enableLimit);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -101,7 +105,11 @@ void ConstraintPrismatic2D::SetLowerTranslation(float lowerTranslation)
 
     jointDef_.lowerTranslation = lowerTranslation;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2PrismaticJoint*>(joint_)->SetLimits(lowerTranslation, jointDef_.upperTranslation);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -112,7 +120,11 @@ void ConstraintPrismatic2D::SetUpperTranslation(float upperTranslation)
 
     jointDef_.upperTranslation = upperTranslation;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2PrismaticJoint*>(joint_)->SetLimits(jointDef_.lowerTranslation, upperTranslation);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -123,7 +135,11 @@ void ConstraintPrismatic2D::SetEnableMotor(bool enableMotor)
 
     jointDef_.enableMotor = enableMotor;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2PrismaticJoint*>(joint_)->EnableMotor(enableMotor);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -134,7 +150,11 @@ void ConstraintPrismatic2D::SetMaxMotorForce(float maxMotorForce)
 
     jointDef_.maxMotorForce = maxMotorForce;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2PrismaticJoint*>(joint_)->SetMaxMotorForce(maxMotorForce);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -145,7 +165,11 @@ void ConstraintPrismatic2D::SetMotorSpeed(float motorSpeed)
 
     jointDef_.motorSpeed = motorSpeed;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2PrismaticJoint*>(joint_)->SetMotorSpeed(motorSpeed);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintPrismatic2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintPulley2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintPulley2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 31 - 7
Source/Atomic/Atomic2D/ConstraintRevolute2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -77,7 +77,11 @@ void ConstraintRevolute2D::SetEnableLimit(bool enableLimit)
 
     jointDef_.enableLimit = enableLimit;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2RevoluteJoint*>(joint_)->EnableLimit(enableLimit);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -88,7 +92,11 @@ void ConstraintRevolute2D::SetLowerAngle(float lowerAngle)
 
     jointDef_.lowerAngle = lowerAngle;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2RevoluteJoint*>(joint_)->SetLimits(lowerAngle, jointDef_.upperAngle);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -99,7 +107,11 @@ void ConstraintRevolute2D::SetUpperAngle(float upperAngle)
 
     jointDef_.upperAngle = upperAngle;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2RevoluteJoint*>(joint_)->SetLimits(jointDef_.lowerAngle, upperAngle);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -110,7 +122,11 @@ void ConstraintRevolute2D::SetEnableMotor(bool enableMotor)
 
     jointDef_.enableMotor = enableMotor;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2RevoluteJoint*>(joint_)->EnableMotor(enableMotor);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -121,7 +137,11 @@ void ConstraintRevolute2D::SetMotorSpeed(float motorSpeed)
 
     jointDef_.motorSpeed = motorSpeed;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2RevoluteJoint*>(joint_)->SetMotorSpeed(motorSpeed);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -132,7 +152,11 @@ void ConstraintRevolute2D::SetMaxMotorTorque(float maxMotorTorque)
 
     jointDef_.maxMotorTorque = maxMotorTorque;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2RevoluteJoint*>(joint_)->SetMaxMotorTorque(maxMotorTorque);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintRevolute2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 6 - 2
Source/Atomic/Atomic2D/ConstraintRope2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -87,7 +87,11 @@ void ConstraintRope2D::SetMaxLength(float maxLength)
 
     jointDef_.maxLength = maxLength;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2RopeJoint*>(joint_)->SetMaxLength(maxLength);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintRope2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

+ 11 - 3
Source/Atomic/Atomic2D/ConstraintWeld2D.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -73,7 +73,11 @@ void ConstraintWeld2D::SetFrequencyHz(float frequencyHz)
 
     jointDef_.frequencyHz = frequencyHz;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2WeldJoint*>(joint_)->SetFrequency(frequencyHz);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 
@@ -84,7 +88,11 @@ void ConstraintWeld2D::SetDampingRatio(float dampingRatio)
 
     jointDef_.dampingRatio = dampingRatio;
 
-    RecreateJoint();
+    if (joint_)
+        static_cast<b2WeldJoint*>(joint_)->SetDampingRatio(dampingRatio);
+    else
+        RecreateJoint();
+
     MarkNetworkUpdate();
 }
 

+ 1 - 1
Source/Atomic/Atomic2D/ConstraintWeld2D.h

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2016 the Urho3D project.
+// Copyright (c) 2008-2017 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal

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