Browse Source

Merge remote-tracking branch 'remotes/mike3d/master'

Lasse Öörni 11 years ago
parent
commit
0cbf491a48
60 changed files with 1655 additions and 939 deletions
  1. 16 6
      Bin/Data/LuaScripts/01_HelloWorld.lua
  2. 11 7
      Bin/Data/LuaScripts/02_HelloGUI.lua
  3. 24 14
      Bin/Data/LuaScripts/03_Sprites.lua
  4. 2 6
      Bin/Data/LuaScripts/04_StaticScene.lua
  5. 2 7
      Bin/Data/LuaScripts/05_AnimatingScene.lua
  6. 28 19
      Bin/Data/LuaScripts/06_SkeletalAnimation.lua
  7. 18 8
      Bin/Data/LuaScripts/07_Billboards.lua
  8. 26 9
      Bin/Data/LuaScripts/08_Decals.lua
  9. 49 7
      Bin/Data/LuaScripts/09_MultipleViewports.lua
  10. 5 9
      Bin/Data/LuaScripts/10_RenderToTexture.lua
  11. 28 11
      Bin/Data/LuaScripts/11_Physics.lua
  12. 26 9
      Bin/Data/LuaScripts/12_PhysicsStressTest.lua
  13. 25 8
      Bin/Data/LuaScripts/13_Ragdolls.lua
  14. 25 14
      Bin/Data/LuaScripts/14_SoundEffects.lua
  15. 92 24
      Bin/Data/LuaScripts/15_Navigation.lua
  16. 29 16
      Bin/Data/LuaScripts/16_Chat.lua
  17. 10 4
      Bin/Data/LuaScripts/17_SceneReplication.lua
  18. 98 44
      Bin/Data/LuaScripts/18_CharacterDemo.lua
  19. 38 23
      Bin/Data/LuaScripts/19_VehicleDemo.lua
  20. 30 12
      Bin/Data/LuaScripts/20_HugeObjectCount.lua
  21. 0 4
      Bin/Data/LuaScripts/23_Water.lua
  22. 28 5
      Bin/Data/LuaScripts/24_Urho2DSprite.lua
  23. 15 2
      Bin/Data/LuaScripts/25_Urho2DParticle.lua
  24. 27 14
      Bin/Data/LuaScripts/26_ConsoleInput.lua
  25. 29 6
      Bin/Data/LuaScripts/27_Urho2DPhysics.lua
  26. 29 5
      Bin/Data/LuaScripts/28_Urho2DPhysicsRope.lua
  27. 0 5
      Bin/Data/LuaScripts/30_LightAnimation.lua
  28. 5 10
      Bin/Data/LuaScripts/31_MaterialAnimation.lua
  29. 95 13
      Bin/Data/LuaScripts/Utilities/Sample.lua
  30. 17 139
      Bin/Data/LuaScripts/Utilities/Touch.lua
  31. 8 1
      Bin/Data/Scripts/01_HelloWorld.as
  32. 9 1
      Bin/Data/Scripts/02_HelloGUI.as
  33. 8 0
      Bin/Data/Scripts/03_Sprites.as
  34. 5 6
      Bin/Data/Scripts/04_StaticScene.as
  35. 3 5
      Bin/Data/Scripts/05_AnimatingScene.as
  36. 22 15
      Bin/Data/Scripts/06_SkeletalAnimation.as
  37. 17 10
      Bin/Data/Scripts/07_Billboards.as
  38. 29 14
      Bin/Data/Scripts/08_Decals.as
  39. 50 10
      Bin/Data/Scripts/09_MultipleViewports.as
  40. 5 6
      Bin/Data/Scripts/10_RenderToTexture.as
  41. 27 12
      Bin/Data/Scripts/11_Physics.as
  42. 25 10
      Bin/Data/Scripts/12_PhysicsStressTest.as
  43. 43 28
      Bin/Data/Scripts/13_Ragdolls.as
  44. 22 14
      Bin/Data/Scripts/14_SoundEffects.as
  45. 79 13
      Bin/Data/Scripts/15_Navigation.as
  46. 28 17
      Bin/Data/Scripts/16_Chat.as
  47. 44 40
      Bin/Data/Scripts/17_SceneReplication.as
  48. 108 49
      Bin/Data/Scripts/18_CharacterDemo.as
  49. 31 9
      Bin/Data/Scripts/19_VehicleDemo.as
  50. 23 6
      Bin/Data/Scripts/20_HugeObjectCount.as
  51. 3 4
      Bin/Data/Scripts/23_Water.as
  52. 28 7
      Bin/Data/Scripts/24_Urho2DSprite.as
  53. 13 2
      Bin/Data/Scripts/25_Urho2DParticle.as
  54. 11 0
      Bin/Data/Scripts/26_ConsoleInput.as
  55. 28 8
      Bin/Data/Scripts/27_Urho2DPhysics.as
  56. 26 6
      Bin/Data/Scripts/28_Urho2DPhysicsRope.as
  57. 6 8
      Bin/Data/Scripts/30_LightAnimation.as
  58. 6 8
      Bin/Data/Scripts/31_MaterialAnimation.as
  59. 100 7
      Bin/Data/Scripts/Utilities/Sample.as
  60. 21 163
      Bin/Data/Scripts/Utilities/Touch.as

+ 16 - 6
Bin/Data/LuaScripts/01_HelloWorld.lua

@@ -9,10 +9,10 @@ require "LuaScripts/Utilities/Sample"
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
-    
+
     -- Create "Hello World" Text
     -- Create "Hello World" Text
     CreateText()
     CreateText()
-    
+
     -- Finally, hook-up this HelloWorld instance to handle update events
     -- Finally, hook-up this HelloWorld instance to handle update events
     SubscribeToEvents()
     SubscribeToEvents()
 end
 end
@@ -27,11 +27,11 @@ function CreateText()
     -- Set font and text color
     -- Set font and text color
     helloText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 30)
     helloText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 30)
     helloText.color = Color(0.0, 1.0, 0.0)
     helloText.color = Color(0.0, 1.0, 0.0)
-    
+
     -- Align Text center-screen
     -- Align Text center-screen
-    helloText.horizontalAlignment = HA_CENTER;
-    helloText.verticalAlignment = VA_CENTER;
-    
+    helloText.horizontalAlignment = HA_CENTER
+    helloText.verticalAlignment = VA_CENTER
+
     -- Add Text instance to the UI root element
     -- Add Text instance to the UI root element
     ui.root:AddChild(helloText)
     ui.root:AddChild(helloText)
 end
 end
@@ -44,3 +44,13 @@ end
 function HandleUpdate(eventType, eventData)
 function HandleUpdate(eventType, eventData)
     -- Do nothing for now, could be extended to eg. animate the display
     -- Do nothing for now, could be extended to eg. animate the display
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 11 - 7
Bin/Data/LuaScripts/02_HelloGUI.lua

@@ -11,12 +11,6 @@
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
 local window = nil
 local window = nil
-
-local cache = GetCache()
-local engine = GetEngine()
-local input = GetInput()
-local ui = GetUI()
-
 local dragBeginPosition = IntVector2(0, 0)
 local dragBeginPosition = IntVector2(0, 0)
 
 
 function Start()
 function Start()
@@ -110,7 +104,7 @@ function InitWindow()
 
 
     -- Subscribe to buttonClose release (following a 'press') events
     -- Subscribe to buttonClose release (following a 'press') events
     SubscribeToEvent(buttonClose, "Released", "HandleClosePressed")
     SubscribeToEvent(buttonClose, "Released", "HandleClosePressed")
-    
+
     -- Subscribe also to all UI mouse clicks just to see where we have clicked
     -- Subscribe also to all UI mouse clicks just to see where we have clicked
     SubscribeToEvent("UIMouseClick", "HandleControlClicked")
     SubscribeToEvent("UIMouseClick", "HandleControlClicked")
 end
 end
@@ -176,3 +170,13 @@ function HandleControlClicked(eventType, eventData)
     -- Update the Window's title text
     -- Update the Window's title text
     windowTitle.text = "Hello " .. name .. "!"
     windowTitle.text = "Hello " .. name .. "!"
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 24 - 14
Bin/Data/LuaScripts/03_Sprites.lua

@@ -15,47 +15,47 @@ local VAR_VELOCITY = ShortStringHash("Velocity")
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
-    
+
     -- Create the sprites to the user interface
     -- Create the sprites to the user interface
-    CreateSprites();
+    CreateSprites()
 
 
     -- Hook up to the frame update events
     -- Hook up to the frame update events
-    SubscribeToEvents();
+    SubscribeToEvents()
 end
 end
 
 
 function CreateSprites()
 function CreateSprites()
     local decalTex = cache:GetResource("Texture2D", "Textures/UrhoDecal.dds")
     local decalTex = cache:GetResource("Texture2D", "Textures/UrhoDecal.dds")
-    
+
     local width = graphics.width
     local width = graphics.width
     local height = graphics.height
     local height = graphics.height
-    
+
     for i = 1, numSprites do
     for i = 1, numSprites do
         -- Create a new sprite, set it to use the texture
         -- Create a new sprite, set it to use the texture
         local sprite = Sprite:new()
         local sprite = Sprite:new()
         sprite.texture = decalTex
         sprite.texture = decalTex
-        sprite:SetFullImageRect()       
-        
+        sprite:SetFullImageRect()
+
         -- The UI root element is as big as the rendering window, set random position within it
         -- The UI root element is as big as the rendering window, set random position within it
         sprite.position = Vector2(Random(width), Random(height))
         sprite.position = Vector2(Random(width), Random(height))
-        
+
         -- Set sprite size & hotspot in its center
         -- Set sprite size & hotspot in its center
         sprite:SetSize(128, 128)
         sprite:SetSize(128, 128)
         sprite.hotSpot = IntVector2(64, 64)
         sprite.hotSpot = IntVector2(64, 64)
-        
+
         -- Set random rotation in degrees and random scale
         -- Set random rotation in degrees and random scale
         sprite.rotation = Random(360.0)
         sprite.rotation = Random(360.0)
         sprite.scale = Vector2(1.0, 1.0) * (Random(1.0) + 0.5)
         sprite.scale = Vector2(1.0, 1.0) * (Random(1.0) + 0.5)
-        
+
         -- Set random color and additive blending mode
         -- Set random color and additive blending mode
         sprite:SetColor(Color(Random(0.5) + 0.5, Random(0.5) + 0.5, Random(0.5) + 0.5, 1.0))
         sprite:SetColor(Color(Random(0.5) + 0.5, Random(0.5) + 0.5, Random(0.5) + 0.5, 1.0))
         sprite.blendMode = BLEND_ADD
         sprite.blendMode = BLEND_ADD
 
 
         -- Add as a child of the root UI element
         -- Add as a child of the root UI element
         ui.root:AddChild(sprite)
         ui.root:AddChild(sprite)
-        
+
         -- Store sprite's velocity as a custom variable
         -- Store sprite's velocity as a custom variable
         sprite:SetVar(VAR_VELOCITY, Variant(Vector2(Random(200.0) - 100.0, Random(200.0) - 100.0)))
         sprite:SetVar(VAR_VELOCITY, Variant(Vector2(Random(200.0) - 100.0, Random(200.0) - 100.0)))
-        
+
         table.insert(sprites, sprite)
         table.insert(sprites, sprite)
     end
     end
 end
 end
@@ -72,10 +72,10 @@ function MoveSprites(timeStep)
     for i = 1, numSprites do
     for i = 1, numSprites do
         local sprite = sprites[i]
         local sprite = sprites[i]
         sprite.rotation = sprite.rotation + timeStep * 30
         sprite.rotation = sprite.rotation + timeStep * 30
-        
+
         local newPos = sprite.position
         local newPos = sprite.position
         newPos = newPos + sprite:GetVar(VAR_VELOCITY):GetVector2() * timeStep
         newPos = newPos + sprite:GetVar(VAR_VELOCITY):GetVector2() * timeStep
-        
+
         if newPos.x >= width then
         if newPos.x >= width then
             newPos.x = newPos.x - width
             newPos.x = newPos.x - width
         elseif newPos.x < 0 then
         elseif newPos.x < 0 then
@@ -95,3 +95,13 @@ function HandleUpdate(eventType, eventData)
 
 
     MoveSprites(timeStep)
     MoveSprites(timeStep)
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 2 - 6
Bin/Data/LuaScripts/04_StaticScene.lua

@@ -3,14 +3,10 @@
 --     - Creating a 3D scene with static content
 --     - Creating a 3D scene with static content
 --     - Displaying the scene using the Renderer subsystem
 --     - Displaying the scene using the Renderer subsystem
 --     - Handling keyboard and mouse input to move a freelook camera
 --     - Handling keyboard and mouse input to move a freelook camera
+--     - Applying a material shader to animate vegetation (simulate wind)
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -68,7 +64,7 @@ function CreateScene()
         mushroomNode:SetScale(0.5 + Random(2.0))
         mushroomNode:SetScale(0.5 + Random(2.0))
         local mushroomObject = mushroomNode:CreateComponent("StaticModel")
         local mushroomObject = mushroomNode:CreateComponent("StaticModel")
         mushroomObject.model = cache:GetResource("Model", "Models/Mushroom.mdl")
         mushroomObject.model = cache:GetResource("Model", "Models/Mushroom.mdl")
-        mushroomObject.material = cache:GetResource("Material", "Materials/Mushroom.xml")
+        mushroomObject.material = cache:GetResource("Material", "Materials/MushroomWind.xml") -- Apply Vegetation Windy shader
     end
     end
 
 
     -- Create a scene node for the camera, which we will move around
     -- Create a scene node for the camera, which we will move around

+ 2 - 7
Bin/Data/LuaScripts/05_AnimatingScene.lua

@@ -6,11 +6,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -59,9 +54,9 @@ function CreateScene()
         boxObject.material = cache:GetResource("Material", "Materials/Stone.xml")
         boxObject.material = cache:GetResource("Material", "Materials/Stone.xml")
 
 
         -- Add the Rotator script object which will rotate the scene node each frame, when the scene sends its update event.
         -- Add the Rotator script object which will rotate the scene node each frame, when the scene sends its update event.
-        -- This requires the C++ component LuaScriptInstance in the scene node, which acts as a container. We need to tell the 
+        -- This requires the C++ component LuaScriptInstance in the scene node, which acts as a container. We need to tell the
         -- class name to instantiate the object
         -- class name to instantiate the object
-        
+
 
 
         local object = boxNode:CreateScriptObject("Rotator")
         local object = boxNode:CreateScriptObject("Rotator")
         object.rotationSpeed = {10.0, 20.0, 30.0}
         object.rotationSpeed = {10.0, 20.0, 30.0}

+ 28 - 19
Bin/Data/LuaScripts/06_SkeletalAnimation.lua

@@ -8,22 +8,16 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
 
 
     -- Create the scene content
     -- Create the scene content
     CreateScene()
     CreateScene()
-    
+
     -- Create the UI content
     -- Create the UI content
     CreateInstructions()
     CreateInstructions()
-    
+
     -- Setup the viewport for displaying the scene
     -- Setup the viewport for displaying the scene
     SetupViewport()
     SetupViewport()
 
 
@@ -33,7 +27,7 @@ end
 
 
 function CreateScene()
 function CreateScene()
     scene_ = Scene()
     scene_ = Scene()
-    
+
     -- Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     -- Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     -- Also create a DebugRenderer component so that we can draw debug geometry
     -- Also create a DebugRenderer component so that we can draw debug geometry
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("Octree")
@@ -45,7 +39,7 @@ function CreateScene()
     local planeObject = planeNode:CreateComponent("StaticModel")
     local planeObject = planeNode:CreateComponent("StaticModel")
     planeObject.model = cache:GetResource("Model", "Models/Plane.mdl")
     planeObject.model = cache:GetResource("Model", "Models/Plane.mdl")
     planeObject.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
     planeObject.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
-    
+
     -- Create a Zone component for ambient lighting & fog control
     -- Create a Zone component for ambient lighting & fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -54,7 +48,7 @@ function CreateScene()
     zone.fogColor = Color(0.5, 0.5, 0.7)
     zone.fogColor = Color(0.5, 0.5, 0.7)
     zone.fogStart = 100.0
     zone.fogStart = 100.0
     zone.fogEnd = 300.0
     zone.fogEnd = 300.0
-    
+
     -- Create a directional light to the world. Enable cascaded shadows on it
     -- Create a directional light to the world. Enable cascaded shadows on it
     local lightNode = scene_:CreateChild("DirectionalLight")
     local lightNode = scene_:CreateChild("DirectionalLight")
     lightNode.direction = Vector3(0.6, -1.0, 0.8)
     lightNode.direction = Vector3(0.6, -1.0, 0.8)
@@ -64,13 +58,13 @@ function CreateScene()
     light.shadowBias = BiasParameters(0.00025, 0.5)
     light.shadowBias = BiasParameters(0.00025, 0.5)
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
-    
+
     -- Create animated models
     -- Create animated models
     local uint NUM_MODELS = 100
     local uint NUM_MODELS = 100
     local MODEL_MOVE_SPEED = 2.0
     local MODEL_MOVE_SPEED = 2.0
     local MODEL_ROTATE_SPEED = 100.0
     local MODEL_ROTATE_SPEED = 100.0
     local bounds = BoundingBox(Vector3(-47.0, 0.0, -47.0), Vector3(47.0, 0.0, 47.0))
     local bounds = BoundingBox(Vector3(-47.0, 0.0, -47.0), Vector3(47.0, 0.0, 47.0))
-    
+
     for i = 1, NUM_MODELS do
     for i = 1, NUM_MODELS do
         local modelNode = scene_:CreateChild("Jack")
         local modelNode = scene_:CreateChild("Jack")
         modelNode.position = Vector3(Random(90.0) - 45.0, 0.0, Random(90.0) - 45.0)
         modelNode.position = Vector3(Random(90.0) - 45.0, 0.0, Random(90.0) - 45.0)
@@ -79,7 +73,7 @@ function CreateScene()
         modelObject.model = cache:GetResource("Model", "Models/Jack.mdl")
         modelObject.model = cache:GetResource("Model", "Models/Jack.mdl")
         modelObject.material = cache:GetResource("Material", "Materials/Jack.xml")
         modelObject.material = cache:GetResource("Material", "Materials/Jack.xml")
         modelObject.castShadows = true
         modelObject.castShadows = true
-        
+
         -- Create an AnimationState for a walk animation. Its time position will need to be manually updated to advance the
         -- Create an AnimationState for a walk animation. Its time position will need to be manually updated to advance the
         -- animation, The alternative would be to use an AnimationController component which updates the animation automatically,
         -- animation, The alternative would be to use an AnimationController component which updates the animation automatically,
         -- but we need to update the model's position manually in any case
         -- but we need to update the model's position manually in any case
@@ -89,7 +83,7 @@ function CreateScene()
         state.weight = 1.0
         state.weight = 1.0
         state.looped = true
         state.looped = true
 
 
-        -- Create our Mover script object that will move & animate the model during each frame's update. 
+        -- Create our Mover script object that will move & animate the model during each frame's update.
 
 
         local object = modelNode:CreateScriptObject("Mover")
         local object = modelNode:CreateScriptObject("Mover")
         object:SetParameters(MODEL_MOVE_SPEED, MODEL_ROTATE_SPEED, bounds)
         object:SetParameters(MODEL_MOVE_SPEED, MODEL_ROTATE_SPEED, bounds)
@@ -99,7 +93,7 @@ function CreateScene()
     cameraNode = scene_:CreateChild("Camera")
     cameraNode = scene_:CreateChild("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     camera.farClip = 300.0
     camera.farClip = 300.0
-    
+
     -- Set an initial position for the camera scene node above the plane
     -- Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0, 5.0, 0.0)
     cameraNode.position = Vector3(0.0, 5.0, 0.0)
 end
 end
@@ -128,7 +122,7 @@ end
 function SubscribeToEvents()
 function SubscribeToEvents()
     -- Subscribe HandleUpdate() function for processing update events
     -- Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate")
     SubscribeToEvent("Update", "HandleUpdate")
-    
+
     -- Subscribe HandlePostRenderUpdate() function for processing the post-render update event, sent after Renderer subsystem is
     -- Subscribe HandlePostRenderUpdate() function for processing the post-render update event, sent after Renderer subsystem is
     -- done with defining the draw calls for the viewports (but before actually executing them.) We will request debug geometry
     -- done with defining the draw calls for the viewports (but before actually executing them.) We will request debug geometry
     -- rendering during that event
     -- rendering during that event
@@ -209,14 +203,14 @@ end
 function Mover:Update(timeStep)
 function Mover:Update(timeStep)
     local node = self:GetNode()
     local node = self:GetNode()
     node:Translate(Vector3(0.0, 0.0, 1.0) * self.moveSpeed * timeStep)
     node:Translate(Vector3(0.0, 0.0, 1.0) * self.moveSpeed * timeStep)
-    
+
     -- If in risk of going outside the plane, rotate the model right
     -- If in risk of going outside the plane, rotate the model right
     local pos = node.position
     local pos = node.position
     local bounds = self.bounds
     local bounds = self.bounds
     if pos.x < bounds.min.x or pos.x > bounds.max.x or pos.z < bounds.min.z or pos.z > bounds.max.z then
     if pos.x < bounds.min.x or pos.x > bounds.max.x or pos.z < bounds.min.z or pos.z > bounds.max.z then
         node:Yaw(self.rotationSpeed * timeStep)
         node:Yaw(self.rotationSpeed * timeStep)
     end
     end
-    
+
     -- Get the model's first (only) animation state and advance its time
     -- Get the model's first (only) animation state and advance its time
     local model = node:GetComponent("AnimatedModel")
     local model = node:GetComponent("AnimatedModel")
     local state = model:GetAnimationState(0)
     local state = model:GetAnimationState(0)
@@ -224,3 +218,18 @@ function Mover:Update(timeStep)
         state:AddTime(timeStep)
         state:AddTime(timeStep)
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>"..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />"..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>"..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">"..
+        "        <element type=\"Text\">"..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />"..
+        "            <attribute name=\"Text\" value=\"SPACE\" />"..
+        "        </element>"..
+        "    </add>"..
+        "</patch>"
+end

+ 18 - 8
Bin/Data/LuaScripts/07_Billboards.lua

@@ -6,13 +6,8 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
 local lightNodes = {}
 local lightNodes = {}
 local billboardNodes = {}
 local billboardNodes = {}
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
 
 
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
@@ -20,7 +15,7 @@ function Start()
 
 
     -- Create the scene content
     -- Create the scene content
     CreateScene()
     CreateScene()
-    
+
     -- Create the UI content
     -- Create the UI content
     CreateInstructions()
     CreateInstructions()
 
 
@@ -111,7 +106,7 @@ function CreateScene()
 
 
         -- After modifying the billboards, they need to be "commited" so that the BillboardSet updates its internals
         -- After modifying the billboards, they need to be "commited" so that the BillboardSet updates its internals
         billboardObject:Commit()
         billboardObject:Commit()
-        
+
         table.insert(billboardNodes, smokeNode)
         table.insert(billboardNodes, smokeNode)
     end
     end
 
 
@@ -148,7 +143,7 @@ function CreateScene()
         -- The spot lights will not have anything near them, so move the near plane of the shadow camera farther
         -- The spot lights will not have anything near them, so move the near plane of the shadow camera farther
         -- for better shadow depth resolution
         -- for better shadow depth resolution
         light.shadowNearFarRatio = 0.01
         light.shadowNearFarRatio = 0.01
-        
+
         table.insert(lightNodes, lightNode)
         table.insert(lightNodes, lightNode)
     end
     end
 
 
@@ -270,3 +265,18 @@ function HandlePostRenderUpdate(eventType, eventData)
         renderer:DrawDebugGeometry(true)
         renderer:DrawDebugGeometry(true)
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 26 - 9
Bin/Data/LuaScripts/08_Decals.lua

@@ -7,12 +7,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -93,7 +87,7 @@ function CreateScene()
             boxObject.occluder = true
             boxObject.occluder = true
         end
         end
     end
     end
-    
+
     -- Create the camera. Limit far clip distance to match the fog
     -- Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_:CreateChild("Camera")
     cameraNode = scene_:CreateChild("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     local camera = cameraNode:CreateComponent("Camera")
@@ -112,7 +106,7 @@ function CreateUI()
     ui.cursor = cursor
     ui.cursor = cursor
     -- Set starting position of the cursor at the rendering window center
     -- Set starting position of the cursor at the rendering window center
     cursor:SetPosition(graphics.width / 2, graphics.height / 2)
     cursor:SetPosition(graphics.width / 2, graphics.height / 2)
-    
+
     -- Construct new Text object, set string to display and font to use
     -- Construct new Text object, set string to display and font to use
     local instructionText = ui.root:CreateChild("Text")
     local instructionText = ui.root:CreateChild("Text")
     instructionText.text =
     instructionText.text =
@@ -153,7 +147,7 @@ function MoveCamera(timeStep)
     if ui.focusElement ~= nil then
     if ui.focusElement ~= nil then
         return
         return
     end
     end
-    
+
     -- Movement speed as world units per second
     -- Movement speed as world units per second
     local MOVE_SPEED = 20.0
     local MOVE_SPEED = 20.0
     -- Mouse sensitivity as degrees per pixel
     -- Mouse sensitivity as degrees per pixel
@@ -253,3 +247,26 @@ function HandlePostRenderUpdate(eventType, eventData)
         renderer:DrawDebugGeometry(false)
         renderer:DrawDebugGeometry(false)
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Paint</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 49 - 7
Bin/Data/LuaScripts/09_MultipleViewports.lua

@@ -5,12 +5,7 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
 local rearCameraNode = nil
 local rearCameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
 
 
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
@@ -36,7 +31,7 @@ function CreateScene()
     -- Also create a DebugRenderer component so that we can draw debug geometry
     -- Also create a DebugRenderer component so that we can draw debug geometry
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("DebugRenderer")
     scene_:CreateComponent("DebugRenderer")
-    
+
     -- Create scene node & StaticModel component for showing a static plane
     -- Create scene node & StaticModel component for showing a static plane
     local planeNode = scene_:CreateChild("Plane")
     local planeNode = scene_:CreateChild("Plane")
     planeNode.scale = Vector3(100.0, 1.0, 100.0)
     planeNode.scale = Vector3(100.0, 1.0, 100.0)
@@ -136,7 +131,7 @@ function SetupViewports()
     -- Set up the front camera viewport
     -- Set up the front camera viewport
     local viewport = Viewport:new(scene_, cameraNode:GetComponent("Camera"))
     local viewport = Viewport:new(scene_, cameraNode:GetComponent("Camera"))
     renderer:SetViewport(0, viewport)
     renderer:SetViewport(0, viewport)
-    
+
     -- Clone the default render path so that we do not interfere with the other viewport, then add
     -- Clone the default render path so that we do not interfere with the other viewport, then add
     -- bloom and FXAA post process effects to the front viewport. Render path commands can be tagged
     -- bloom and FXAA post process effects to the front viewport. Render path commands can be tagged
     -- for example with the effect name to allow easy toggling on and off. We start with the effects
     -- for example with the effect name to allow easy toggling on and off. We start with the effects
@@ -229,3 +224,50 @@ function HandlePostRenderUpdate(eventType, eventData)
         renderer:DrawDebugGeometry(false)
         renderer:DrawDebugGeometry(false)
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element\">" ..
+        "        <element type=\"Button\">" ..
+        "            <attribute name=\"Name\" value=\"Button3\" />" ..
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" ..
+        "            <attribute name=\"Size\" value=\"96 96\" />" ..
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" ..
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" ..
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" ..
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" ..
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" ..
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"Label\" />" ..
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" ..
+        "                <attribute name=\"Text\" value=\"FXAA\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"F\" />" ..
+        "            </element>" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Bloom</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"B\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 5 - 9
Bin/Data/LuaScripts/10_RenderToTexture.lua

@@ -5,12 +5,8 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
 local rttScene_ = nil
 local rttScene_ = nil
-local cameraNode = nil
 local rttCameraNode = nil
 local rttCameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
 
 
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
@@ -67,7 +63,7 @@ function CreateScene()
     rttCameraNode = rttScene_:CreateChild("Camera")
     rttCameraNode = rttScene_:CreateChild("Camera")
     local camera = rttCameraNode:CreateComponent("Camera")
     local camera = rttCameraNode:CreateComponent("Camera")
     camera.farClip = 100.0
     camera.farClip = 100.0
-    
+
     -- Create a point light to the camera scene node
     -- Create a point light to the camera scene node
     local light = rttCameraNode:CreateComponent("Light")
     local light = rttCameraNode:CreateComponent("Light")
     light.lightType = LIGHT_POINT
     light.lightType = LIGHT_POINT
@@ -94,7 +90,7 @@ function CreateScene()
     light.lightType = LIGHT_DIRECTIONAL
     light.lightType = LIGHT_DIRECTIONAL
     light.color = Color(0.2, 0.2, 0.2)
     light.color = Color(0.2, 0.2, 0.2)
     light.specularIntensity = 1.0
     light.specularIntensity = 1.0
-    
+
     -- Create a "floor" consisting of several tiles
     -- Create a "floor" consisting of several tiles
     for y = -5, 5 do
     for y = -5, 5 do
         for x = -5, 5 do
         for x = -5, 5 do
@@ -127,7 +123,7 @@ function CreateScene()
     local renderTexture = Texture2D:new()
     local renderTexture = Texture2D:new()
     renderTexture:SetSize(1024, 768, Graphics:GetRGBFormat(), TEXTURE_RENDERTARGET)
     renderTexture:SetSize(1024, 768, Graphics:GetRGBFormat(), TEXTURE_RENDERTARGET)
     renderTexture.filterMode = FILTER_BILINEAR
     renderTexture.filterMode = FILTER_BILINEAR
-    
+
     -- Create a new material from scratch, use the diffuse unlit technique, assign the render texture
     -- Create a new material from scratch, use the diffuse unlit technique, assign the render texture
     -- as its diffuse texture, then assign the material to the screen plane object
     -- as its diffuse texture, then assign the material to the screen plane object
     local renderMaterial = Material:new()
     local renderMaterial = Material:new()
@@ -186,7 +182,7 @@ function MoveCamera(timeStep)
     yaw = yaw + MOUSE_SENSITIVITY * mouseMove.x
     yaw = yaw + MOUSE_SENSITIVITY * mouseMove.x
     pitch = pitch + MOUSE_SENSITIVITY * mouseMove.y
     pitch = pitch + MOUSE_SENSITIVITY * mouseMove.y
     pitch = Clamp(pitch, -90.0, 90.0)
     pitch = Clamp(pitch, -90.0, 90.0)
-    
+
     -- Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
     -- Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
     cameraNode.rotation = Quaternion(pitch, yaw, 0.0)
     cameraNode.rotation = Quaternion(pitch, yaw, 0.0)
     -- Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     -- Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
@@ -213,7 +209,7 @@ end
 function HandleUpdate(eventType, eventData)
 function HandleUpdate(eventType, eventData)
     -- Take the frame time step, which is stored as a float
     -- Take the frame time step, which is stored as a float
     local timeStep = eventData:GetFloat("TimeStep")
     local timeStep = eventData:GetFloat("TimeStep")
-    
+
     -- Move the camera, scale movement with time step
     -- Move the camera, scale movement with time step
     MoveCamera(timeStep)
     MoveCamera(timeStep)
 end
 end

+ 28 - 11
Bin/Data/LuaScripts/11_Physics.lua

@@ -7,12 +7,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -40,7 +34,7 @@ function CreateScene()
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("PhysicsWorld")
     scene_:CreateComponent("PhysicsWorld")
     scene_:CreateComponent("DebugRenderer")
     scene_:CreateComponent("DebugRenderer")
-    
+
     -- Create a Zone component for ambient lighting & fog control
     -- Create a Zone component for ambient lighting & fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -59,7 +53,7 @@ function CreateScene()
     light.shadowBias = BiasParameters(0.00025, 0.5)
     light.shadowBias = BiasParameters(0.00025, 0.5)
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
-    
+
     -- Create skybox. The Skybox component is used like StaticModel, but it will be always located at the camera, giving the
     -- Create skybox. The Skybox component is used like StaticModel, but it will be always located at the camera, giving the
     -- illusion of the box planes being far away. Use just the ordinary Box model and a suitable material, whose shader will
     -- illusion of the box planes being far away. Use just the ordinary Box model and a suitable material, whose shader will
     -- generate the necessary 3D texture coordinates for cube mapping
     -- generate the necessary 3D texture coordinates for cube mapping
@@ -85,7 +79,7 @@ function CreateScene()
     -- Set a box shape of size 1 x 1 x 1 for collision. The shape will be scaled with the scene node scale, so the
     -- Set a box shape of size 1 x 1 x 1 for collision. The shape will be scaled with the scene node scale, so the
     -- rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
     -- rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
     shape:SetBox(Vector3(1.0, 1.0, 1.0))
     shape:SetBox(Vector3(1.0, 1.0, 1.0))
-    
+
     -- Create a pyramid of movable physics objects
     -- Create a pyramid of movable physics objects
     for y = 0, 7 do
     for y = 0, 7 do
         for x = -y, y do
         for x = -y, y do
@@ -182,12 +176,12 @@ function MoveCamera(timeStep)
     if input:GetKeyDown(KEY_D) then
     if input:GetKeyDown(KEY_D) then
         cameraNode:Translate(Vector3(1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
         cameraNode:Translate(Vector3(1.0, 0.0, 0.0) * MOVE_SPEED * timeStep)
     end
     end
-    
+
     -- "Shoot" a physics object with left mousebutton
     -- "Shoot" a physics object with left mousebutton
     if input:GetMouseButtonPress(MOUSEB_LEFT) then
     if input:GetMouseButtonPress(MOUSEB_LEFT) then
         SpawnObject()
         SpawnObject()
     end
     end
-    
+
     -- Check for loading/saving the scene. Save the scene to the file Data/Scenes/Physics.xml relative to the executable
     -- Check for loading/saving the scene. Save the scene to the file Data/Scenes/Physics.xml relative to the executable
     -- directory
     -- directory
     if input:GetKeyPress(KEY_F5) then
     if input:GetKeyPress(KEY_F5) then
@@ -244,3 +238,26 @@ function HandlePostRenderUpdate(eventType, eventData)
         scene_:GetComponent("PhysicsWorld"):DrawDebugGeometry(true)
         scene_:GetComponent("PhysicsWorld"):DrawDebugGeometry(true)
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 26 - 9
Bin/Data/LuaScripts/12_PhysicsStressTest.lua

@@ -7,12 +7,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -40,7 +34,7 @@ function CreateScene()
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("PhysicsWorld")
     scene_:CreateComponent("PhysicsWorld")
     scene_:CreateComponent("DebugRenderer")
     scene_:CreateComponent("DebugRenderer")
-    
+
     -- Create a Zone component for ambient lighting & fog control
     -- Create a Zone component for ambient lighting & fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -49,7 +43,7 @@ function CreateScene()
     zone.fogColor = Color(0.5, 0.5, 0.7)
     zone.fogColor = Color(0.5, 0.5, 0.7)
     zone.fogStart = 100.0
     zone.fogStart = 100.0
     zone.fogEnd = 300.0
     zone.fogEnd = 300.0
-    
+
     -- Create a directional light to the world. Enable cascaded shadows on it
     -- Create a directional light to the world. Enable cascaded shadows on it
     local lightNode = scene_:CreateChild("DirectionalLight")
     local lightNode = scene_:CreateChild("DirectionalLight")
     lightNode.direction = Vector3(0.6, -1.0, 0.8)
     lightNode.direction = Vector3(0.6, -1.0, 0.8)
@@ -59,7 +53,7 @@ function CreateScene()
     light.shadowBias = BiasParameters(0.00025, 0.5)
     light.shadowBias = BiasParameters(0.00025, 0.5)
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     -- Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
     light.shadowCascade = CascadeParameters(10.0, 50.0, 200.0, 0.0, 0.8)
-    
+
     if true then
     if true then
         -- Create a floor object, 500 x 500 world units. Adjust position so that the ground is at zero Y
         -- Create a floor object, 500 x 500 world units. Adjust position so that the ground is at zero Y
         local floorNode = scene_:CreateChild("Floor")
         local floorNode = scene_:CreateChild("Floor")
@@ -258,3 +252,26 @@ function HandlePostRenderUpdate(eventType, eventData)
         scene_:GetComponent("PhysicsWorld"):DrawDebugGeometry(true)
         scene_:GetComponent("PhysicsWorld"):DrawDebugGeometry(true)
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 25 - 8
Bin/Data/LuaScripts/13_Ragdolls.lua

@@ -6,12 +6,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -89,7 +83,7 @@ function CreateScene()
             modelObject.castShadows = true
             modelObject.castShadows = true
             -- Set the model to also update when invisible to avoid staying invisible when the model should come into
             -- Set the model to also update when invisible to avoid staying invisible when the model should come into
             -- view, but does not as the bounding box is not updated
             -- view, but does not as the bounding box is not updated
-            modelObject.updateInvisible = true;
+            modelObject.updateInvisible = true
 
 
             -- Create a rigid body and a collision shape. These will act as a trigger for transforming the
             -- Create a rigid body and a collision shape. These will act as a trigger for transforming the
             -- model into a ragdoll when hit by a moving object
             -- model into a ragdoll when hit by a moving object
@@ -124,7 +118,7 @@ function CreateInstructions()
         "Use WASD keys and mouse to move\n"..
         "Use WASD keys and mouse to move\n"..
         "LMB to spawn physics objects\n"..
         "LMB to spawn physics objects\n"..
         "F5 to save scene, F7 to load\n"..
         "F5 to save scene, F7 to load\n"..
-        "Space to toggle physics debug geometry");
+        "Space to toggle physics debug geometry")
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     -- The text has multiple rows. Center them in relation to each other
     -- The text has multiple rows. Center them in relation to each other
     instructionText.textAlignment = HA_CENTER
     instructionText.textAlignment = HA_CENTER
@@ -371,3 +365,26 @@ function CreateRagdoll:CreateRagdollConstraint(boneName, parentName, type, axis,
     constraint.highLimit = highLimit
     constraint.highLimit = highLimit
     constraint.lowLimit = lowLimit
     constraint.lowLimit = lowLimit
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 25 - 14
Bin/Data/LuaScripts/14_SoundEffects.lua

@@ -5,8 +5,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-
 local soundNames = {
 local soundNames = {
     "Fist",
     "Fist",
     "Explosion",
     "Explosion",
@@ -18,11 +16,11 @@ local soundResourceNames = {
     "Sounds/BigExplosion.wav",
     "Sounds/BigExplosion.wav",
     "Sounds/Powerup.wav"
     "Sounds/Powerup.wav"
     }
     }
-    
+
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
-    
+
     -- Enable OS cursor
     -- Enable OS cursor
     input.mouseVisible = true
     input.mouseVisible = true
 
 
@@ -37,7 +35,7 @@ function CreateUI()
     local uiStyle = cache:GetResource("XMLFile", "UI/DefaultStyle.xml")
     local uiStyle = cache:GetResource("XMLFile", "UI/DefaultStyle.xml")
     -- Set style to the UI root so that elements will inherit it
     -- Set style to the UI root so that elements will inherit it
     ui.root.defaultStyle = uiStyle
     ui.root.defaultStyle = uiStyle
-    
+
     -- Create buttons for playing back sounds
     -- Create buttons for playing back sounds
     for i, v in ipairs(soundNames) do
     for i, v in ipairs(soundNames) do
         local button = CreateButton((i - 1) * 140 + 20, 20, 120, 40, v)
         local button = CreateButton((i - 1) * 140 + 20, 20, 120, 40, v)
@@ -45,11 +43,11 @@ function CreateUI()
         button:SetVar(ShortStringHash("SoundResource"), Variant(soundResourceNames[i]))
         button:SetVar(ShortStringHash("SoundResource"), Variant(soundResourceNames[i]))
         SubscribeToEvent(button, "Pressed", "HandlePlaySound")
         SubscribeToEvent(button, "Pressed", "HandlePlaySound")
     end
     end
-    
+
     -- Create buttons for playing/stopping music
     -- Create buttons for playing/stopping music
     local button = CreateButton(20, 80, 120, 40, "Play Music")
     local button = CreateButton(20, 80, 120, 40, "Play Music")
     SubscribeToEvent(button, "Released", "HandlePlayMusic")
     SubscribeToEvent(button, "Released", "HandlePlayMusic")
-    
+
     button = CreateButton(160, 80, 120, 40, "Stop Music")
     button = CreateButton(160, 80, 120, 40, "Stop Music")
     SubscribeToEvent(button, "Released", "HandleStopMusic")
     SubscribeToEvent(button, "Released", "HandleStopMusic")
 
 
@@ -57,7 +55,7 @@ function CreateUI()
     local slider = CreateSlider(20, 140, 200, 20, "Sound Volume")
     local slider = CreateSlider(20, 140, 200, 20, "Sound Volume")
     slider.value = audio:GetMasterGain(SOUND_EFFECT)
     slider.value = audio:GetMasterGain(SOUND_EFFECT)
     SubscribeToEvent(slider, "SliderChanged", "HandleSoundVolume")
     SubscribeToEvent(slider, "SliderChanged", "HandleSoundVolume")
-    
+
     slider = CreateSlider(20, 200, 200, 20, "Music Volume")
     slider = CreateSlider(20, 200, 200, 20, "Music Volume")
     slider.value = audio:GetMasterGain(SOUND_MUSIC)
     slider.value = audio:GetMasterGain(SOUND_MUSIC)
     SubscribeToEvent(slider, "SliderChanged", "HandleMusicVolume")
     SubscribeToEvent(slider, "SliderChanged", "HandleMusicVolume")
@@ -65,18 +63,18 @@ end
 
 
 function CreateButton(x, y, xSize, ySize, text)
 function CreateButton(x, y, xSize, ySize, text)
     local font = cache:GetResource("Font", "Fonts/Anonymous Pro.ttf")
     local font = cache:GetResource("Font", "Fonts/Anonymous Pro.ttf")
-    
+
     -- Create the button and center the text onto it
     -- Create the button and center the text onto it
     local button = ui.root:CreateChild("Button")
     local button = ui.root:CreateChild("Button")
     button:SetStyleAuto()
     button:SetStyleAuto()
     button:SetPosition(x, y)
     button:SetPosition(x, y)
     button:SetSize(xSize, ySize)
     button:SetSize(xSize, ySize)
-    
+
     local buttonText = button:CreateChild("Text")
     local buttonText = button:CreateChild("Text")
     buttonText:SetAlignment(HA_CENTER, VA_CENTER)
     buttonText:SetAlignment(HA_CENTER, VA_CENTER)
     buttonText:SetFont(font, 12)
     buttonText:SetFont(font, 12)
     buttonText:SetText(text)
     buttonText:SetText(text)
-    
+
     return button
     return button
 end
 end
 
 
@@ -88,14 +86,14 @@ function CreateSlider(x, y, xSize, ySize, text)
     sliderText:SetPosition(x, y)
     sliderText:SetPosition(x, y)
     sliderText:SetFont(font, 12)
     sliderText:SetFont(font, 12)
     sliderText:SetText(text)
     sliderText:SetText(text)
-    
+
     local slider = ui.root:CreateChild("Slider")
     local slider = ui.root:CreateChild("Slider")
     slider:SetStyleAuto()
     slider:SetStyleAuto()
     slider:SetPosition(x, y + 20)
     slider:SetPosition(x, y + 20)
     slider:SetSize(xSize, ySize)
     slider:SetSize(xSize, ySize)
     -- Use 0-1 range for controlling sound/music master volume
     -- Use 0-1 range for controlling sound/music master volume
     slider.range = 1.0
     slider.range = 1.0
-    
+
     return slider
     return slider
 end
 end
 
 
@@ -130,7 +128,7 @@ function HandlePlayMusic(eventType, eventData)
     local music = cache:GetResource("Sound", "Music/Ninja Gods.ogg")
     local music = cache:GetResource("Sound", "Music/Ninja Gods.ogg")
     -- Set the song to loop
     -- Set the song to loop
     music.looped = true
     music.looped = true
-    
+
     -- Create a scene node and a sound source for the music
     -- Create a scene node and a sound source for the music
     local musicNode = scene_:CreateChild("Music")
     local musicNode = scene_:CreateChild("Music")
     local musicSource = musicNode:CreateComponent("SoundSource")
     local musicSource = musicNode:CreateComponent("SoundSource")
@@ -153,3 +151,16 @@ function HandleMusicVolume(eventType, eventData)
     local newVolume = eventData:GetFloat("Value")
     local newVolume = eventData:GetFloat("Value")
     audio:SetMasterGain(SOUND_MUSIC, newVolume)
     audio:SetMasterGain(SOUND_MUSIC, newVolume)
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 92 - 24
Bin/Data/LuaScripts/15_Navigation.lua

@@ -9,13 +9,8 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
 local endPos = nil
 local endPos = nil
 local currentPath = {}
 local currentPath = {}
-local yaw = 0.0
-local pitch = 0.0
-local drawDebug = false
 
 
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
@@ -23,10 +18,10 @@ function Start()
 
 
     -- Create the scene content
     -- Create the scene content
     CreateScene()
     CreateScene()
-    
+
     -- Create the UI content
     -- Create the UI content
     CreateUI()
     CreateUI()
-    
+
     -- Setup the viewport for displaying the scene
     -- Setup the viewport for displaying the scene
     SetupViewport()
     SetupViewport()
 
 
@@ -40,14 +35,14 @@ function CreateScene()
     -- Also create a DebugRenderer component so that we can draw debug geometry
     -- Also create a DebugRenderer component so that we can draw debug geometry
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("Octree")
     scene_:CreateComponent("DebugRenderer")
     scene_:CreateComponent("DebugRenderer")
-    
+
     -- Create scene node & StaticModel component for showing a static plane
     -- Create scene node & StaticModel component for showing a static plane
     local planeNode = scene_:CreateChild("Plane")
     local planeNode = scene_:CreateChild("Plane")
     planeNode.scale = Vector3(100.0, 1.0, 100.0)
     planeNode.scale = Vector3(100.0, 1.0, 100.0)
     local planeObject = planeNode:CreateComponent("StaticModel")
     local planeObject = planeNode:CreateComponent("StaticModel")
     planeObject.model = cache:GetResource("Model", "Models/Plane.mdl")
     planeObject.model = cache:GetResource("Model", "Models/Plane.mdl")
     planeObject.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
     planeObject.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
-    
+
     -- Create a Zone component for ambient lighting & fog control
     -- Create a Zone component for ambient lighting & fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -72,7 +67,7 @@ function CreateScene()
     for i = 1, NUM_MUSHROOMS do
     for i = 1, NUM_MUSHROOMS do
         CreateMushroom(Vector3(Random(90.0) - 45.0, 0.0, Random(90.0) - 45.0))
         CreateMushroom(Vector3(Random(90.0) - 45.0, 0.0, Random(90.0) - 45.0))
     end
     end
-    
+
     -- Create randomly sized boxes. If boxes are big enough, make them occluders. Occluders will be software rasterized before
     -- Create randomly sized boxes. If boxes are big enough, make them occluders. Occluders will be software rasterized before
     -- rendering to a low-resolution depth-only buffer to test the objects in the view frustum for visibility
     -- rendering to a low-resolution depth-only buffer to test the objects in the view frustum for visibility
     local NUM_BOXES = 20
     local NUM_BOXES = 20
@@ -110,12 +105,12 @@ function CreateScene()
     -- physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example)
     -- physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example)
     -- it will use renderable geometry instead
     -- it will use renderable geometry instead
     navMesh:Build()
     navMesh:Build()
-    
+
     -- Create the camera. Limit far clip distance to match the fog
     -- Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_:CreateChild("Camera")
     cameraNode = scene_:CreateChild("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     camera.farClip = 300.0
     camera.farClip = 300.0
-    
+
     -- Set an initial position for the camera scene node above the plane
     -- Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0, 5.0, 0.0)
     cameraNode.position = Vector3(0.0, 5.0, 0.0)
 end
 end
@@ -129,7 +124,7 @@ function CreateUI()
     ui.cursor = cursor
     ui.cursor = cursor
     -- Set starting position of the cursor at the rendering window center
     -- Set starting position of the cursor at the rendering window center
     cursor:SetPosition(graphics.width / 2, graphics.height / 2)
     cursor:SetPosition(graphics.width / 2, graphics.height / 2)
-    
+
     -- Construct new Text object, set string to display and font to use
     -- Construct new Text object, set string to display and font to use
     local instructionText = ui.root:CreateChild("Text")
     local instructionText = ui.root:CreateChild("Text")
     instructionText.text = "Use WASD keys to move, RMB to rotate view\n"..
     instructionText.text = "Use WASD keys to move, RMB to rotate view\n"..
@@ -169,12 +164,12 @@ function MoveCamera(timeStep)
     if ui.focusElement ~= nil then
     if ui.focusElement ~= nil then
         return
         return
     end
     end
-    
+
     -- Movement speed as world units per second
     -- Movement speed as world units per second
     local MOVE_SPEED = 20.0
     local MOVE_SPEED = 20.0
     -- Mouse sensitivity as degrees per pixel
     -- Mouse sensitivity as degrees per pixel
     local MOUSE_SENSITIVITY = 0.1
     local MOUSE_SENSITIVITY = 0.1
-    
+
     -- Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
     -- Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
     -- Only move the camera when the cursor is hidden
     -- Only move the camera when the cursor is hidden
     if not ui.cursor.visible then
     if not ui.cursor.visible then
@@ -224,11 +219,11 @@ function SetPathPoint()
             -- Teleport
             -- Teleport
             currentPath = {}
             currentPath = {}
             jackNode:LookAt(Vector3(pathPos.x, jackNode.position.y, pathPos.z), Vector3(0.0, 1.0, 0.0))
             jackNode:LookAt(Vector3(pathPos.x, jackNode.position.y, pathPos.z), Vector3(0.0, 1.0, 0.0))
-            jackNode.position = pathPos;
+            jackNode.position = pathPos
         else
         else
             -- Calculate path from Jack's current position to the end point
             -- Calculate path from Jack's current position to the end point
-            endPos = pathPos;
-            currentPath = navMesh:FindPath(jackNode.position, endPos);
+            endPos = pathPos
+            currentPath = navMesh:FindPath(jackNode.position, endPos)
         end
         end
     end
     end
 end
 end
@@ -240,7 +235,7 @@ function AddOrRemoveObject()
         -- The part of the navigation mesh we must update, which is the world bounding box of the associated
         -- The part of the navigation mesh we must update, which is the world bounding box of the associated
         -- drawable component
         -- drawable component
         local updateBox = nil
         local updateBox = nil
-        
+
         local hitNode = hitDrawable:GetNode()
         local hitNode = hitDrawable:GetNode()
         if hitNode.name == "Mushroom" then
         if hitNode.name == "Mushroom" then
             updateBox = hitDrawable.worldBoundingBox
             updateBox = hitDrawable.worldBoundingBox
@@ -250,12 +245,12 @@ function AddOrRemoveObject()
             local newObject = newNode:GetComponent("StaticModel")
             local newObject = newNode:GetComponent("StaticModel")
             updateBox = newObject.worldBoundingBox
             updateBox = newObject.worldBoundingBox
         end
         end
-        
+
         -- Rebuild part of the navigation mesh, then recalculate path if applicable
         -- Rebuild part of the navigation mesh, then recalculate path if applicable
         local navMesh = scene_:GetComponent("NavigationMesh")
         local navMesh = scene_:GetComponent("NavigationMesh")
         navMesh:Build(updateBox)
         navMesh:Build(updateBox)
         if table.maxn(currentPath) > 0 then
         if table.maxn(currentPath) > 0 then
-            currentPath = navMesh:FindPath(jackNode.position, endPos);
+            currentPath = navMesh:FindPath(jackNode.position, endPos)
         end
         end
     end
     end
 end
 end
@@ -293,7 +288,7 @@ function Raycast(maxDistance)
         hitDrawable = result.drawable
         hitDrawable = result.drawable
         return true, hitPos, hitDrawable
         return true, hitPos, hitDrawable
     end
     end
-    
+
     return false, nil, nil
     return false, nil, nil
 end
 end
 
 
@@ -335,13 +330,13 @@ function HandlePostRenderUpdate(eventType, eventData)
         local navMesh = scene_:GetComponent("NavigationMesh")
         local navMesh = scene_:GetComponent("NavigationMesh")
         navMesh:DrawDebugGeometry(true)
         navMesh:DrawDebugGeometry(true)
     end
     end
-    
+
     -- Visualize the start and end points and the last calculated path
     -- Visualize the start and end points and the last calculated path
     local size = table.maxn(currentPath)
     local size = table.maxn(currentPath)
     if size > 0 then
     if size > 0 then
         local debug = scene_:GetComponent("DebugRenderer")
         local debug = scene_:GetComponent("DebugRenderer")
         debug:AddBoundingBox(BoundingBox(endPos - Vector3(0.1, 0.1, 0.1), endPos + Vector3(0.1, 0.1, 0.1)), Color(1.0, 1.0, 1.0))
         debug:AddBoundingBox(BoundingBox(endPos - Vector3(0.1, 0.1, 0.1), endPos + Vector3(0.1, 0.1, 0.1)), Color(1.0, 1.0, 1.0))
-        
+
         -- Draw the path with a small upward bias so that it does not clip into the surfaces
         -- Draw the path with a small upward bias so that it does not clip into the surfaces
         local bias = Vector3(0.0, 0.05, 0.0)
         local bias = Vector3(0.0, 0.05, 0.0)
         debug:AddLine(jackNode.position + bias, currentPath[1] + bias, Color(1.0, 1.0, 1.0))
         debug:AddLine(jackNode.position + bias, currentPath[1] + bias, Color(1.0, 1.0, 1.0))
@@ -353,3 +348,76 @@ function HandlePostRenderUpdate(eventType, eventData)
         end
         end
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element\">" ..
+        "        <element type=\"Button\">" ..
+        "            <attribute name=\"Name\" value=\"Button3\" />" ..
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" ..
+        "            <attribute name=\"Size\" value=\"96 96\" />" ..
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" ..
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" ..
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" ..
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" ..
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" ..
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"Label\" />" ..
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" ..
+        "                <attribute name=\"Text\" value=\"Teleport\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"LSHIFT\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "            </element>" ..
+        "        </element>" ..
+        "        <element type=\"Button\">" ..
+        "            <attribute name=\"Name\" value=\"Button4\" />" ..
+        "            <attribute name=\"Position\" value=\"-120 -12\" />" ..
+        "            <attribute name=\"Size\" value=\"96 96\" />" ..
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" ..
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" ..
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" ..
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" ..
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" ..
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"Label\" />" ..
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" ..
+        "                <attribute name=\"Text\" value=\"Obstacles\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"MIDDLE\" />" ..
+        "            </element>" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Set</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"LEFT\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 29 - 16
Bin/Data/LuaScripts/16_Chat.lua

@@ -28,7 +28,7 @@ function Start()
 
 
     -- Create the user interface
     -- Create the user interface
     CreateUI()
     CreateUI()
-    
+
     -- Subscribe to UI and network events
     -- Subscribe to UI and network events
     SubscribeToEvents()
     SubscribeToEvents()
 end
 end
@@ -48,22 +48,22 @@ function CreateUI()
     buttonContainer:SetFixedSize(graphics.width, 20)
     buttonContainer:SetFixedSize(graphics.width, 20)
     buttonContainer:SetPosition(0, graphics.height - 20)
     buttonContainer:SetPosition(0, graphics.height - 20)
     buttonContainer.layoutMode = LM_HORIZONTAL
     buttonContainer.layoutMode = LM_HORIZONTAL
-    
+
     textEdit = buttonContainer:CreateChild("LineEdit")
     textEdit = buttonContainer:CreateChild("LineEdit")
     textEdit:SetStyleAuto()
     textEdit:SetStyleAuto()
-    
+
     sendButton = CreateButton("Send", 70)
     sendButton = CreateButton("Send", 70)
     connectButton = CreateButton("Connect", 90)
     connectButton = CreateButton("Connect", 90)
     disconnectButton = CreateButton("Disconnect", 100)
     disconnectButton = CreateButton("Disconnect", 100)
     startServerButton = CreateButton("Start Server", 110)
     startServerButton = CreateButton("Start Server", 110)
 
 
     UpdateButtons()
     UpdateButtons()
-    
+
     local size = (graphics.height - 20) / chatHistoryText.rowHeight
     local size = (graphics.height - 20) / chatHistoryText.rowHeight
     for i = 1, size do
     for i = 1, size do
         table.insert(chatHistory, "")
         table.insert(chatHistory, "")
     end
     end
-    
+
     -- No viewports or scene is defined. However, the default zone's fog color controls the fill color
     -- No viewports or scene is defined. However, the default zone's fog color controls the fill color
     renderer.defaultZone.fogColor = Color(0.0, 0.0, 0.1)
     renderer.defaultZone.fogColor = Color(0.0, 0.0, 0.1)
 end
 end
@@ -78,7 +78,7 @@ function SubscribeToEvents()
 
 
     -- Subscribe to log messages so that we can pipe them to the chat window
     -- Subscribe to log messages so that we can pipe them to the chat window
     SubscribeToEvent("LogMessage", "HandleLogMessage")
     SubscribeToEvent("LogMessage", "HandleLogMessage")
-    
+
     -- Subscribe to network events
     -- Subscribe to network events
     SubscribeToEvent("NetworkMessage", "HandleNetworkMessage")
     SubscribeToEvent("NetworkMessage", "HandleNetworkMessage")
     SubscribeToEvent("ServerConnected", "HandleConnectionStatus")
     SubscribeToEvent("ServerConnected", "HandleConnectionStatus")
@@ -88,7 +88,7 @@ end
 
 
 function CreateButton(text, width)
 function CreateButton(text, width)
     local font = cache:GetResource("Font", "Fonts/Anonymous Pro.ttf")
     local font = cache:GetResource("Font", "Fonts/Anonymous Pro.ttf")
-    
+
     local button = buttonContainer:CreateChild("Button")
     local button = buttonContainer:CreateChild("Button")
     button:SetStyleAuto()
     button:SetStyleAuto()
     button:SetFixedWidth(width)
     button:SetFixedWidth(width)
@@ -97,14 +97,14 @@ function CreateButton(text, width)
     buttonText:SetFont(font, 12)
     buttonText:SetFont(font, 12)
     buttonText:SetAlignment(HA_CENTER, VA_CENTER)
     buttonText:SetAlignment(HA_CENTER, VA_CENTER)
     buttonText.text = text
     buttonText.text = text
-    
+
     return button
     return button
 end
 end
 
 
 function ShowChatText(row)
 function ShowChatText(row)
     table.remove(chatHistory, 1)
     table.remove(chatHistory, 1)
     table.insert(chatHistory, row)
     table.insert(chatHistory, row)
-    
+
     -- Concatenate all the rows in history
     -- Concatenate all the rows in history
     local allRows = ""
     local allRows = ""
     for i, r in ipairs(chatHistory) do
     for i, r in ipairs(chatHistory) do
@@ -116,7 +116,7 @@ end
 function UpdateButtons()
 function UpdateButtons()
     local serverConnection = network.serverConnection
     local serverConnection = network.serverConnection
     local serverRunning = network.serverRunning
     local serverRunning = network.serverRunning
-    
+
     -- Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     -- Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     sendButton.visible = serverConnection ~= nil
     sendButton.visible = serverConnection ~= nil
     connectButton.visible = (serverConnection == nil) and (not serverRunning)
     connectButton.visible = (serverConnection == nil) and (not serverRunning)
@@ -151,15 +151,15 @@ function HandleConnect(eventType, eventData)
     if address == "" then
     if address == "" then
         address = "localhost" -- Use localhost to connect if nothing else specified
         address = "localhost" -- Use localhost to connect if nothing else specified
     end
     end
-        
+
     -- Empty the text edit after reading the address to connect to
     -- Empty the text edit after reading the address to connect to
     textEdit.text = ""
     textEdit.text = ""
-    
+
     -- Connect to server, do not specify a client scene as we are not using scene replication, just messages.
     -- Connect to server, do not specify a client scene as we are not using scene replication, just messages.
     -- At connect time we could also send identity parameters (such as username) in a VariantMap, but in this
     -- At connect time we could also send identity parameters (such as username) in a VariantMap, but in this
     -- case we skip it for simplicity
     -- case we skip it for simplicity
     network:Connect(address, CHAT_SERVER_PORT, nil)
     network:Connect(address, CHAT_SERVER_PORT, nil)
-    
+
     UpdateButtons()
     UpdateButtons()
 end
 end
 
 
@@ -174,13 +174,13 @@ function HandleDisconnect(eventType, eventData)
             network:StopServer()
             network:StopServer()
         end
         end
     end
     end
-    
+
     UpdateButtons()
     UpdateButtons()
 end
 end
 
 
 function HandleStartServer(eventType, eventData)
 function HandleStartServer(eventType, eventData)
     network:StartServer(CHAT_SERVER_PORT)
     network:StartServer(CHAT_SERVER_PORT)
-    
+
     UpdateButtons()
     UpdateButtons()
 end
 end
 
 
@@ -205,4 +205,17 @@ end
 
 
 function HandleConnectionStatus(eventType, eventData)
 function HandleConnectionStatus(eventType, eventData)
     UpdateButtons()
     UpdateButtons()
-end
+end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 10 - 4
Bin/Data/LuaScripts/17_SceneReplication.lua

@@ -16,8 +16,6 @@ local CTRL_BACK = 2
 local CTRL_LEFT = 4
 local CTRL_LEFT = 4
 local CTRL_RIGHT = 8
 local CTRL_RIGHT = 8
 
 
-local scene_ = nil
-local cameraNode = nil
 local instructionsText = nil
 local instructionsText = nil
 local buttonContainer = nil
 local buttonContainer = nil
 local textEdit = nil
 local textEdit = nil
@@ -25,8 +23,6 @@ local connectButton = nil
 local disconnectButton = nil
 local disconnectButton = nil
 local startServerButton = nil
 local startServerButton = nil
 local clients = {}
 local clients = {}
-local yaw = 0.0
-local pitch = 1.0
 local clientObjectID = 0
 local clientObjectID = 0
 
 
 function Start()
 function Start()
@@ -392,3 +388,13 @@ end
 function HandleClientObjectID(eventType, eventData)
 function HandleClientObjectID(eventType, eventData)
     clientObjectID = eventData:GetUInt("ID")
     clientObjectID = eventData:GetUInt("ID")
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 98 - 44
Bin/Data/LuaScripts/18_CharacterDemo.lua

@@ -16,7 +16,7 @@ CTRL_FORWARD = 1
 CTRL_BACK = 2
 CTRL_BACK = 2
 CTRL_LEFT = 4
 CTRL_LEFT = 4
 CTRL_RIGHT = 8
 CTRL_RIGHT = 8
-CTRL_JUMP = 16
+local CTRL_JUMP = 16
 
 
 local MOVE_FORCE = 0.8
 local MOVE_FORCE = 0.8
 local INAIR_MOVE_FORCE = 0.02
 local INAIR_MOVE_FORCE = 0.02
@@ -24,9 +24,9 @@ local BRAKE_FORCE = 0.2
 local JUMP_FORCE = 7.0
 local JUMP_FORCE = 7.0
 local YAW_SENSITIVITY = 0.1
 local YAW_SENSITIVITY = 0.1
 local INAIR_THRESHOLD_TIME = 0.1
 local INAIR_THRESHOLD_TIME = 0.1
+firstPerson = false -- First person camera flag
 
 
-scene_ = nil
-characterNode = nil
+local characterNode = nil
 
 
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
@@ -41,12 +41,6 @@ function Start()
     -- Create the UI content
     -- Create the UI content
     CreateInstructions()
     CreateInstructions()
 
 
-    -- Activate mobile stuff only when appropriate
-    if GetPlatform() == "Android" or GetPlatform() == "iOS" then 
-        SetLogoVisible(false)
-        InitTouchInput()
-    end
-
     -- Subscribe to necessary events
     -- Subscribe to necessary events
     SubscribeToEvents()
     SubscribeToEvents()
 end
 end
@@ -152,7 +146,7 @@ function CreateCharacter()
     characterNode:CreateComponent("AnimationController")
     characterNode:CreateComponent("AnimationController")
 
 
     -- Set the head bone for manual control
     -- Set the head bone for manual control
-    object.skeleton:GetBone("Bip01_Head").animated = false;
+    object.skeleton:GetBone("Bip01_Head").animated = false
 
 
     -- Create rigidbody, and set non-zero mass so that the body becomes dynamic
     -- Create rigidbody, and set non-zero mass so that the body becomes dynamic
     local body = characterNode:CreateComponent("RigidBody")
     local body = characterNode:CreateComponent("RigidBody")
@@ -198,10 +192,8 @@ function SubscribeToEvents()
     -- Subscribe to PostUpdate event for updating the camera position after physics simulation
     -- Subscribe to PostUpdate event for updating the camera position after physics simulation
     SubscribeToEvent("PostUpdate", "HandlePostUpdate")
     SubscribeToEvent("PostUpdate", "HandlePostUpdate")
 
 
-    -- Subscribe to mobile touch events
-    if touchEnabled == true then
-        SubscribeToTouchEvents()
-    end
+    -- Unsubscribe the SceneUpdate event from base class as the camera node is being controlled in HandlePostUpdate() in this sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 end
 
 
 function HandleUpdate(eventType, eventData)
 function HandleUpdate(eventType, eventData)
@@ -217,45 +209,60 @@ function HandleUpdate(eventType, eventData)
     -- Clear previous controls
     -- Clear previous controls
     character.controls:Set(CTRL_FORWARD + CTRL_BACK + CTRL_LEFT + CTRL_RIGHT + CTRL_JUMP, false)
     character.controls:Set(CTRL_FORWARD + CTRL_BACK + CTRL_LEFT + CTRL_RIGHT + CTRL_JUMP, false)
 
 
-    if touchEnabled == true then
-        -- Update controls using touch (mobile)
-        updateTouches()
-    else 
-        -- Update controls using keys (desktop)
-        if ui.focusElement == nil then
+    -- Update controls using touch utility
+    if touchEnabled then UpdateTouches(character.controls) end
+
+    -- Update controls using keys
+    if ui.focusElement == nil then
+        if not touchEnabled or not useGyroscope then
             if input:GetKeyDown(KEY_W) then character.controls:Set(CTRL_FORWARD, true) end
             if input:GetKeyDown(KEY_W) then character.controls:Set(CTRL_FORWARD, true) end
             if input:GetKeyDown(KEY_S) then character.controls:Set(CTRL_BACK, true) end
             if input:GetKeyDown(KEY_S) then character.controls:Set(CTRL_BACK, true) end
             if input:GetKeyDown(KEY_A) then character.controls:Set(CTRL_LEFT, true) end
             if input:GetKeyDown(KEY_A) then character.controls:Set(CTRL_LEFT, true) end
             if input:GetKeyDown(KEY_D) then character.controls:Set(CTRL_RIGHT, true) end
             if input:GetKeyDown(KEY_D) then character.controls:Set(CTRL_RIGHT, true) end
-            if input:GetKeyDown(KEY_SPACE) then character.controls:Set(CTRL_JUMP, true) end
-
-            -- Add character yaw & pitch from the mouse motion
+        end
+        if input:GetKeyDown(KEY_SPACE) then character.controls:Set(CTRL_JUMP, true) end
+
+        -- Add character yaw & pitch from the mouse motion or touch input
+        if touchEnabled then
+            for i=0, input.numTouches - 1 do
+                local state = input:GetTouch(i)
+                if not state.touchedElement then -- Touch on empty space
+                    local camera = cameraNode:GetComponent("Camera")
+                    if not camera then return end
+
+                    character.controls.yaw = character.controls.yaw + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x
+                    character.controls.pitch = character.controls.pitch + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y
+                end
+            end
+        else
             character.controls.yaw = character.controls.yaw + input.mouseMoveX * YAW_SENSITIVITY
             character.controls.yaw = character.controls.yaw + input.mouseMoveX * YAW_SENSITIVITY
             character.controls.pitch = character.controls.pitch + input.mouseMoveY * YAW_SENSITIVITY
             character.controls.pitch = character.controls.pitch + input.mouseMoveY * YAW_SENSITIVITY
-            -- Limit pitch
-            character.controls.pitch = Clamp(character.controls.pitch, -80.0, 80.0)
+        end
+        -- Limit pitch
+        character.controls.pitch = Clamp(character.controls.pitch, -80.0, 80.0)
 
 
-            -- Switch between 1st and 3rd person
-            if input:GetKeyPress(KEY_F) then
-                firstPerson = not firstPerson
-                newFirstPerson = firstPerson
-            end
+        -- Switch between 1st and 3rd person
+        if input:GetKeyPress(KEY_F) then
+            firstPerson = not firstPerson
+        end
 
 
-            -- Check for loading / saving the scene
-            if input:GetKeyPress(KEY_F5) then
-                scene_:SaveXML(fileSystem:GetProgramDir().."Data/Scenes/CharacterDemo.xml")
-            end
-            if input:GetKeyPress(KEY_F7) then
-                scene_:LoadXML(fileSystem:GetProgramDir().."Data/Scenes/CharacterDemo.xml")
-                -- After loading we have to reacquire the character scene node, as it has been recreated
-                -- Simply find by name as there's only one of them
-                characterNode = scene_:GetChild("Jack", true)
-                if characterNode == nil then
-                    return
-                end
+        -- Turn on/off gyroscope on mobile platform
+        if input:GetKeyPress(KEY_G) then
+            useGyroscope = not useGyroscope
+        end
+
+        -- Check for loading / saving the scene
+        if input:GetKeyPress(KEY_F5) then
+            scene_:SaveXML(fileSystem:GetProgramDir().."Data/Scenes/CharacterDemo.xml")
+        end
+        if input:GetKeyPress(KEY_F7) then
+            scene_:LoadXML(fileSystem:GetProgramDir().."Data/Scenes/CharacterDemo.xml")
+            -- After loading we have to reacquire the character scene node, as it has been recreated
+            -- Simply find by name as there's only one of them
+            characterNode = scene_:GetChild("Jack", true)
+            if characterNode == nil then
+                return
             end
             end
-        else
-            character.controls:Set(CTRL_FORWARD + CTRL_BACK + CTRL_LEFT + CTRL_RIGHT + CTRL_JUMP, false)
         end
         end
     end
     end
 
 
@@ -434,3 +441,50 @@ function Character:FixedUpdate(timeStep)
     -- Reset grounded flag for next frame
     -- Reset grounded flag for next frame
     self.onGround = false
     self.onGround = false
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element\">" ..
+        "        <element type=\"Button\">" ..
+        "            <attribute name=\"Name\" value=\"Button3\" />" ..
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" ..
+        "            <attribute name=\"Size\" value=\"96 96\" />" ..
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" ..
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" ..
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" ..
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" ..
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" ..
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"Label\" />" ..
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" ..
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" ..
+        "                <attribute name=\"Text\" value=\"Gyroscope\" />" ..
+        "            </element>" ..
+        "            <element type=\"Text\">" ..
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "                <attribute name=\"Text\" value=\"G\" />" ..
+        "            </element>" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">1st/3rd</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"F\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Jump</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 38 - 23
Bin/Data/LuaScripts/19_VehicleDemo.lua

@@ -2,6 +2,7 @@
 -- This sample demonstrates:
 -- This sample demonstrates:
 --     - Creating a heightmap terrain with collision
 --     - Creating a heightmap terrain with collision
 --     - Constructing a physical vehicle with rigid bodies for the hull and the wheels, joined with constraints
 --     - Constructing a physical vehicle with rigid bodies for the hull and the wheels, joined with constraints
+--     - Saving and loading the variables of a script object, including node & component references
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
@@ -16,15 +17,13 @@ local ENGINE_POWER = 10.0
 local DOWN_FORCE = 10.0
 local DOWN_FORCE = 10.0
 local MAX_WHEEL_ANGLE = 22.5
 local MAX_WHEEL_ANGLE = 22.5
 
 
-local scene_ = nil
-local cameraNode = nil
 local vehicleNode = nil
 local vehicleNode = nil
 
 
 function Start()
 function Start()
 
 
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
-    
+
     -- Create static scene content
     -- Create static scene content
     CreateScene()
     CreateScene()
 
 
@@ -49,9 +48,9 @@ function CreateScene()
     cameraNode = Node()
     cameraNode = Node()
     local camera = cameraNode:CreateComponent("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     camera.farClip = 500.0
     camera.farClip = 500.0
-    
+
     renderer:SetViewport(0, Viewport:new(scene_, camera))
     renderer:SetViewport(0, Viewport:new(scene_, camera))
-    
+
     -- Create static scene content. First create a zone for ambient lighting and fog control
     -- Create static scene content. First create a zone for ambient lighting and fog control
     local zoneNode = scene_:CreateChild("Zone")
     local zoneNode = scene_:CreateChild("Zone")
     local zone = zoneNode:CreateComponent("Zone")
     local zone = zoneNode:CreateComponent("Zone")
@@ -83,7 +82,7 @@ function CreateScene()
     -- The terrain consists of large triangles, which fits well for occlusion rendering, as a hill can occlude all
     -- The terrain consists of large triangles, which fits well for occlusion rendering, as a hill can occlude all
     -- terrain patches and other objects behind it
     -- terrain patches and other objects behind it
     terrain.occluder = true
     terrain.occluder = true
-    
+
     local body = terrainNode:CreateComponent("RigidBody")
     local body = terrainNode:CreateComponent("RigidBody")
     body.collisionLayer = 2 -- Use layer bitmask 2 for static geometry
     body.collisionLayer = 2 -- Use layer bitmask 2 for static geometry
     local shape = terrainNode:CreateComponent("CollisionShape")
     local shape = terrainNode:CreateComponent("CollisionShape")
@@ -103,7 +102,7 @@ function CreateScene()
         object.model = cache:GetResource("Model", "Models/Mushroom.mdl")
         object.model = cache:GetResource("Model", "Models/Mushroom.mdl")
         object.material = cache:GetResource("Material", "Materials/Mushroom.xml")
         object.material = cache:GetResource("Material", "Materials/Mushroom.xml")
         object.castShadows = true
         object.castShadows = true
-        
+
         local body = objectNode:CreateComponent("RigidBody")
         local body = objectNode:CreateComponent("RigidBody")
         body.collisionLayer = 2
         body.collisionLayer = 2
         local shape = objectNode:CreateComponent("CollisionShape")
         local shape = objectNode:CreateComponent("CollisionShape")
@@ -114,7 +113,7 @@ end
 function CreateVehicle()
 function CreateVehicle()
     vehicleNode = scene_:CreateChild("Vehicle")
     vehicleNode = scene_:CreateChild("Vehicle")
     vehicleNode.position = Vector3(0.0, 5.0, 0.0)
     vehicleNode.position = Vector3(0.0, 5.0, 0.0)
-    
+
     -- Create the vehicle logic script object
     -- Create the vehicle logic script object
     local vehicle = vehicleNode:CreateScriptObject("Vehicle")
     local vehicle = vehicleNode:CreateScriptObject("Vehicle")
     -- Create the rendering and physics components
     -- Create the rendering and physics components
@@ -124,7 +123,7 @@ end
 function CreateInstructions()
 function CreateInstructions()
     -- Construct new Text object, set string to display and font to use
     -- Construct new Text object, set string to display and font to use
     local instructionText = ui.root:CreateChild("Text")
     local instructionText = ui.root:CreateChild("Text")
-    instructionText.text = "Use WASD keys to drive, mouse to rotate camera\n"..
+    instructionText.text = "Use WASD keys to drive, mouse/touch to rotate camera\n"..
         "F5 to save scene, F7 to load"
         "F5 to save scene, F7 to load"
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     -- The text has multiple rows. Center them in relation to each other
     -- The text has multiple rows. Center them in relation to each other
@@ -142,6 +141,9 @@ function SubscribeToEvents()
 
 
     -- Subscribe to PostUpdate event for updating the camera position after physics simulation
     -- Subscribe to PostUpdate event for updating the camera position after physics simulation
     SubscribeToEvent("PostUpdate", "HandlePostUpdate")
     SubscribeToEvent("PostUpdate", "HandlePostUpdate")
+
+    -- Unsubscribe the SceneUpdate event from base class as the camera node is being controlled in HandlePostUpdate() in this sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 end
 
 
 function HandleUpdate(eventType, eventData)
 function HandleUpdate(eventType, eventData)
@@ -161,9 +163,22 @@ function HandleUpdate(eventType, eventData)
         vehicle.controls:Set(CTRL_LEFT, input:GetKeyDown(KEY_A))
         vehicle.controls:Set(CTRL_LEFT, input:GetKeyDown(KEY_A))
         vehicle.controls:Set(CTRL_RIGHT, input:GetKeyDown(KEY_D))
         vehicle.controls:Set(CTRL_RIGHT, input:GetKeyDown(KEY_D))
 
 
-        -- Add yaw & pitch from the mouse motion. Used only for the camera, does not affect motion
-        vehicle.controls.yaw = vehicle.controls.yaw + input.mouseMoveX * YAW_SENSITIVITY
-        vehicle.controls.pitch = vehicle.controls.pitch + input.mouseMoveY * YAW_SENSITIVITY
+        -- Add yaw & pitch from the mouse motion or touch input. Used only for the camera, does not affect motion
+        if touchEnabled then
+            for i=0, input.numTouches - 1 do
+                local state = input:GetTouch(i)
+                if not state.touchedElement then -- Touch on empty space
+                    local camera = cameraNode:GetComponent("Camera")
+                    if not camera then return end
+
+                    vehicle.controls.yaw = vehicle.controls.yaw + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x
+                    vehicle.controls.pitch = vehicle.controls.pitch + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y
+                end
+            end
+        else
+            vehicle.controls.yaw = vehicle.controls.yaw + input.mouseMoveX * YAW_SENSITIVITY
+            vehicle.controls.pitch = vehicle.controls.pitch + input.mouseMoveY * YAW_SENSITIVITY
+        end
         -- Limit pitch
         -- Limit pitch
         vehicle.controls.pitch = Clamp(vehicle.controls.pitch, 0.0, 80.0)
         vehicle.controls.pitch = Clamp(vehicle.controls.pitch, 0.0, 80.0)
 
 
@@ -187,7 +202,7 @@ function HandlePostUpdate(eventType, eventData)
     if vehicleNode == nil then
     if vehicleNode == nil then
         return
         return
     end
     end
-    
+
     local vehicle = vehicleNode:GetScriptObject()
     local vehicle = vehicleNode:GetScriptObject()
     if vehicle == nil then
     if vehicle == nil then
         return
         return
@@ -197,7 +212,7 @@ function HandlePostUpdate(eventType, eventData)
     local dir = Quaternion(vehicleNode.rotation:YawAngle(), Vector3(0.0, 1.0, 0.0))
     local dir = Quaternion(vehicleNode.rotation:YawAngle(), Vector3(0.0, 1.0, 0.0))
     dir = dir * Quaternion(vehicle.controls.yaw, Vector3(0.0, 1.0, 0.0))
     dir = dir * Quaternion(vehicle.controls.yaw, Vector3(0.0, 1.0, 0.0))
     dir = dir * Quaternion(vehicle.controls.pitch, Vector3(1.0, 0.0, 0.0))
     dir = dir * Quaternion(vehicle.controls.pitch, Vector3(1.0, 0.0, 0.0))
-    
+
     local cameraTargetPos = vehicleNode.position - dir * Vector3(0.0, 0.0, CAMERA_DISTANCE)
     local cameraTargetPos = vehicleNode.position - dir * Vector3(0.0, 0.0, CAMERA_DISTANCE)
     local cameraStartPos = vehicleNode.position
     local cameraStartPos = vehicleNode.position
     -- Raycast camera against static objects (physics collision mask 2)
     -- Raycast camera against static objects (physics collision mask 2)
@@ -244,13 +259,13 @@ function Vehicle:Init()
     local hullObject = node:CreateComponent("StaticModel")
     local hullObject = node:CreateComponent("StaticModel")
     self.hullBody = node:CreateComponent("RigidBody")
     self.hullBody = node:CreateComponent("RigidBody")
     local hullShape = node:CreateComponent("CollisionShape")
     local hullShape = node:CreateComponent("CollisionShape")
-    
+
     node.scale = Vector3(1.5, 1.0, 3.0)
     node.scale = Vector3(1.5, 1.0, 3.0)
     hullObject.model = cache:GetResource("Model", "Models/Box.mdl")
     hullObject.model = cache:GetResource("Model", "Models/Box.mdl")
     hullObject.material = cache:GetResource("Material", "Materials/Stone.xml")
     hullObject.material = cache:GetResource("Material", "Materials/Stone.xml")
     hullObject.castShadows = true
     hullObject.castShadows = true
     hullShape:SetBox(Vector3(1.0, 1.0, 1.0))
     hullShape:SetBox(Vector3(1.0, 1.0, 1.0))
-    
+
     self.hullBody.mass = 4.0
     self.hullBody.mass = 4.0
     self.hullBody.linearDamping = 0.2 -- Some air resistance
     self.hullBody.linearDamping = 0.2 -- Some air resistance
     self.hullBody.angularDamping = 0.5
     self.hullBody.angularDamping = 0.5
@@ -268,12 +283,12 @@ function Vehicle:PostInit()
     self.frontRight = scene_:GetChild("FrontRight")
     self.frontRight = scene_:GetChild("FrontRight")
     self.rearLeft = scene_:GetChild("RearLeft")
     self.rearLeft = scene_:GetChild("RearLeft")
     self.rearRight = scene_:GetChild("RearRight")
     self.rearRight = scene_:GetChild("RearRight")
-    
+
     self.frontLeftAxis = self.frontLeft:GetComponent("Constraint")
     self.frontLeftAxis = self.frontLeft:GetComponent("Constraint")
     self.frontRightAxis = self.frontRight:GetComponent("Constraint")
     self.frontRightAxis = self.frontRight:GetComponent("Constraint")
-    
+
     self.hullBody = self.node:GetComponent("RigidBody")
     self.hullBody = self.node:GetComponent("RigidBody")
-    
+
     self.frontLeftBody = self.frontLeft:GetComponent("RigidBody")
     self.frontLeftBody = self.frontLeft:GetComponent("RigidBody")
     self.frontRightBody = self.frontRight:GetComponent("RigidBody")
     self.frontRightBody = self.frontRight:GetComponent("RigidBody")
     self.rearLeftBody = self.rearLeft:GetComponent("RigidBody")
     self.rearLeftBody = self.rearLeft:GetComponent("RigidBody")
@@ -311,13 +326,13 @@ function Vehicle:InitWheel(name, offset)
     wheelConstraint.otherBody = node:GetComponent("RigidBody")
     wheelConstraint.otherBody = node:GetComponent("RigidBody")
     wheelConstraint.worldPosition = wheelNode.worldPosition -- Set constraint's both ends at wheel's location
     wheelConstraint.worldPosition = wheelNode.worldPosition -- Set constraint's both ends at wheel's location
     wheelConstraint.axis = Vector3(0.0, 1.0, 0.0) -- Wheel rotates around its local Y-axis
     wheelConstraint.axis = Vector3(0.0, 1.0, 0.0) -- Wheel rotates around its local Y-axis
-    
+
     if offset.x >= 0.0 then -- Wheel's hull axis points either left or right
     if offset.x >= 0.0 then -- Wheel's hull axis points either left or right
         wheelConstraint.otherAxis = Vector3(1.0, 0.0, 0.0)
         wheelConstraint.otherAxis = Vector3(1.0, 0.0, 0.0)
     else
     else
         wheelConstraint.otherAxis = Vector3(-1.0, 0.0, 0.0)
         wheelConstraint.otherAxis = Vector3(-1.0, 0.0, 0.0)
     end
     end
-    
+
     wheelConstraint.lowLimit = Vector2(-180.0, 0.0) -- Let the wheel rotate freely around the axis
     wheelConstraint.lowLimit = Vector2(-180.0, 0.0) -- Let the wheel rotate freely around the axis
     wheelConstraint.highLimit = Vector2(180.0, 0.0)
     wheelConstraint.highLimit = Vector2(180.0, 0.0)
     wheelConstraint.disableCollision = true -- Let the wheel intersect the vehicle hull
     wheelConstraint.disableCollision = true -- Let the wheel intersect the vehicle hull
@@ -341,7 +356,7 @@ function Vehicle:FixedUpdate(timeStep)
     if self.controls:IsDown(CTRL_BACK) then
     if self.controls:IsDown(CTRL_BACK) then
         accelerator = -0.5
         accelerator = -0.5
     end
     end
-    
+
     -- When steering, wake up the wheel rigidbodies so that their orientation is updated
     -- When steering, wake up the wheel rigidbodies so that their orientation is updated
     if newSteering ~= 0.0 then
     if newSteering ~= 0.0 then
         self.frontLeftBody:Activate()
         self.frontLeftBody:Activate()
@@ -368,4 +383,4 @@ function Vehicle:FixedUpdate(timeStep)
     -- Apply downforce proportional to velocity
     -- Apply downforce proportional to velocity
     local localVelocity = self.hullBody.rotation:Inverse() * self.hullBody.linearVelocity
     local localVelocity = self.hullBody.rotation:Inverse() * self.hullBody.linearVelocity
     self.hullBody:ApplyForce(self.hullBody.rotation * Vector3(0.0, -1.0, 0.0) * Abs(localVelocity.z) * DOWN_FORCE)
     self.hullBody:ApplyForce(self.hullBody.rotation * Vector3(0.0, -1.0, 0.0) * Abs(localVelocity.z) * DOWN_FORCE)
-end
+end

+ 30 - 12
Bin/Data/LuaScripts/20_HugeObjectCount.lua

@@ -7,11 +7,7 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
 local boxNodes = {}
 local boxNodes = {}
-local yaw = 0.0
-local pitch = 0.0
 local animate = false
 local animate = false
 local useGroups = false
 local useGroups = false
 
 
@@ -51,14 +47,13 @@ function CreateScene()
     zone.fogColor = Color(0.2, 0.2, 0.2)
     zone.fogColor = Color(0.2, 0.2, 0.2)
     zone.fogStart = 200.0
     zone.fogStart = 200.0
     zone.fogEnd = 300.0
     zone.fogEnd = 300.0
-    
+
     -- Create a directional light
     -- Create a directional light
     local lightNode = scene_:CreateChild("DirectionalLight")
     local lightNode = scene_:CreateChild("DirectionalLight")
     lightNode.direction = Vector3(-0.6, -1.0, -0.8) -- The direction vector does not need to be normalized
     lightNode.direction = Vector3(-0.6, -1.0, -0.8) -- The direction vector does not need to be normalized
     local light = lightNode:CreateComponent("Light")
     local light = lightNode:CreateComponent("Light")
     light.lightType = LIGHT_DIRECTIONAL
     light.lightType = LIGHT_DIRECTIONAL
 
 
-
     if not useGroups then
     if not useGroups then
         light.color = Color(0.7, 0.35, 0.0)
         light.color = Color(0.7, 0.35, 0.0)
 
 
@@ -74,8 +69,8 @@ function CreateScene()
             end
             end
         end
         end
     else
     else
-        light.color = Color(0.6, 0.6, 0.6);
-        light.specularIntensity = 1.5;
+        light.color = Color(0.6, 0.6, 0.6)
+        light.specularIntensity = 1.5
 
 
         -- Create StaticModelGroups in the scene
         -- Create StaticModelGroups in the scene
         local lastGroup = nil
         local lastGroup = nil
@@ -91,11 +86,11 @@ function CreateScene()
                     lastGroup.model = cache:GetResource("Model", "Models/Box.mdl")
                     lastGroup.model = cache:GetResource("Model", "Models/Box.mdl")
                 end
                 end
 
 
-                local boxNode = scene_:CreateChild("Box");
+                local boxNode = scene_:CreateChild("Box")
                 boxNode.position = Vector3(x * 0.3, 0.0, y * 0.3)
                 boxNode.position = Vector3(x * 0.3, 0.0, y * 0.3)
                 boxNode:SetScale(0.25)
                 boxNode:SetScale(0.25)
                 table.insert(boxNodes, boxNode)
                 table.insert(boxNodes, boxNode)
-                lastGroup:AddInstanceNode(boxNode);
+                lastGroup:AddInstanceNode(boxNode)
             end
             end
         end
         end
     end
     end
@@ -118,7 +113,7 @@ function CreateInstructions()
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     instructionText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
     -- The text has multiple rows. Center them in relation to each other
     -- The text has multiple rows. Center them in relation to each other
     instructionText.textAlignment = HA_CENTER
     instructionText.textAlignment = HA_CENTER
-    
+
     -- Position the text relative to the screen center
     -- Position the text relative to the screen center
     instructionText.horizontalAlignment = HA_CENTER
     instructionText.horizontalAlignment = HA_CENTER
     instructionText.verticalAlignment = VA_CENTER
     instructionText.verticalAlignment = VA_CENTER
@@ -197,9 +192,32 @@ function HandleUpdate(eventType, eventData)
 
 
     -- Move the camera, scale movement with time step
     -- Move the camera, scale movement with time step
     MoveCamera(timeStep)
     MoveCamera(timeStep)
-    
+
     -- Animate scene if enabled
     -- Animate scene if enabled
     if animate then
     if animate then
         AnimateObjects(timeStep)
         AnimateObjects(timeStep)
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Group</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"G\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Animation</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"SPACE\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 0 - 4
Bin/Data/LuaScripts/23_Water.lua

@@ -5,14 +5,10 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
 local reflectionCameraNode = nil
 local reflectionCameraNode = nil
 local waterNode = nil
 local waterNode = nil
 local waterPlane = Plane()
 local waterPlane = Plane()
 local waterClipPlane = Plane()
 local waterClipPlane = Plane()
-local yaw = 0.0
-local pitch = 0.0
 
 
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples

+ 28 - 5
Bin/Data/LuaScripts/24_Urho2DSprite.lua

@@ -7,9 +7,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -57,7 +54,7 @@ function CreateScene()
     for i = 1, NUM_SPRITES do
     for i = 1, NUM_SPRITES do
         local spriteNode = scene_:CreateChild("StaticSprite2D")
         local spriteNode = scene_:CreateChild("StaticSprite2D")
         spriteNode.position = Vector3(Random(-halfWidth, halfWidth), Random(-halfHeight, halfHeight), 0.0)
         spriteNode.position = Vector3(Random(-halfWidth, halfWidth), Random(-halfHeight, halfHeight), 0.0)
-        
+
         local staticSprite = spriteNode:CreateComponent("StaticSprite2D")
         local staticSprite = spriteNode:CreateComponent("StaticSprite2D")
         -- Set color
         -- Set color
         staticSprite.color = Color(Random(1.0), Random(1.0), Random(1.0), 1.0)
         staticSprite.color = Color(Random(1.0), Random(1.0), Random(1.0), 1.0)
@@ -102,7 +99,7 @@ function CreateScene()
 
 
     local spriteNode = scene_:CreateChild("AnimatedSprite2D")
     local spriteNode = scene_:CreateChild("AnimatedSprite2D")
     spriteNode.position = Vector3(0.0, 0.0, -1.0)
     spriteNode.position = Vector3(0.0, 0.0, -1.0)
-    
+
     local animatedSprite = spriteNode:CreateComponent("AnimatedSprite2D")
     local animatedSprite = spriteNode:CreateComponent("AnimatedSprite2D")
     -- Set animation
     -- Set animation
     animatedSprite.animation = animation
     animatedSprite.animation = animation
@@ -167,6 +164,9 @@ end
 function SubscribeToEvents()
 function SubscribeToEvents()
     -- Subscribe HandleUpdate() function for processing update events
     -- Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate")
     SubscribeToEvent("Update", "HandleUpdate")
+
+    -- Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 end
 
 
 function HandleUpdate(eventType, eventData)
 function HandleUpdate(eventType, eventData)
@@ -179,3 +179,26 @@ function HandleUpdate(eventType, eventData)
     -- Update scene
     -- Update scene
     scene_:Update(timeStep)
     scene_:Update(timeStep)
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 15 - 2
Bin/Data/LuaScripts/25_Urho2DParticle.lua

@@ -6,8 +6,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
 local particleNode = nil
 local particleNode = nil
 
 
 function Start()
 function Start()
@@ -87,7 +85,12 @@ function SetupViewport()
 end
 end
 
 
 function SubscribeToEvents()
 function SubscribeToEvents()
+    -- Subscribe HandleMouseMove() function for tracking mouse/touch move events
     SubscribeToEvent("MouseMove", "HandleMouseMove")
     SubscribeToEvent("MouseMove", "HandleMouseMove")
+    if touchEnabled then SubscribeToEvent("TouchMove", "HandleMouseMove") end
+
+    -- Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 end
 
 
 function HandleMouseMove(eventType, eventData)
 function HandleMouseMove(eventType, eventData)
@@ -98,3 +101,13 @@ function HandleMouseMove(eventType, eventData)
         particleNode.position = camera:ScreenToWorldPoint(Vector3(x / graphics.width, y / graphics.height, 10.0))
         particleNode.position = camera:ScreenToWorldPoint(Vector3(x / graphics.width, y / graphics.height, 10.0))
     end
     end
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 27 - 14
Bin/Data/LuaScripts/26_ConsoleInput.lua

@@ -33,7 +33,7 @@ local urhoThreatLevels = {
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
-    
+
     -- Disable default execution of Lua from the console
     -- Disable default execution of Lua from the console
     SetExecuteConsoleCommands(false)
     SetExecuteConsoleCommands(false)
 
 
@@ -43,17 +43,17 @@ function Start()
 
 
     -- Subscribe key down event
     -- Subscribe key down event
     SubscribeToEvent("KeyDown", "HandleEscKeyDown")
     SubscribeToEvent("KeyDown", "HandleEscKeyDown")
-    
+
     -- Hide logo to make room for the console
     -- Hide logo to make room for the console
     SetLogoVisible(false)
     SetLogoVisible(false)
 
 
     -- Show the console by default, make it large
     -- Show the console by default, make it large
     console.numRows = graphics.height / 16
     console.numRows = graphics.height / 16
-    console.numBufferedRows = 2 * console.numRows;
-    console.commandInterpreter = "LuaScript";
+    console.numBufferedRows = 2 * console.numRows
+    console.commandInterpreter = "LuaScript"
     console.visible = true
     console.visible = true
-    console.closeButton.visible = false;
-    
+    console.closeButton.visible = false
+
     -- Show OS mouse cursor
     -- Show OS mouse cursor
     input.mouseVisible = true
     input.mouseVisible = true
 
 
@@ -67,7 +67,7 @@ function Start()
     StartGame()
     StartGame()
 
 
     -- Randomize from system clock
     -- Randomize from system clock
-    SetRandomSeed(time.systemTime)
+    SetRandomSeed(time:GetSystemTime())
 end
 end
 
 
 function HandleConsoleCommand(eventType, eventData)
 function HandleConsoleCommand(eventType, eventData)
@@ -96,7 +96,7 @@ function StartGame()
           "objective is to survive as long as possible. Beware of hunger and the merciless\n" ..
           "objective is to survive as long as possible. Beware of hunger and the merciless\n" ..
           "predator cichlid Urho, who appears from time to time. Evading Urho is easier\n" ..
           "predator cichlid Urho, who appears from time to time. Evading Urho is easier\n" ..
           "with an empty stomach. Type 'help' for available commands.")
           "with an empty stomach. Type 'help' for available commands.")
-    
+
     gameOn = true
     gameOn = true
     foodAvailable = false
     foodAvailable = false
     eatenLastTurn = false
     eatenLastTurn = false
@@ -109,7 +109,7 @@ function EndGame(message)
     Print(message)
     Print(message)
     Print("Game over! You survived " .. numTurns .. " turns.\n" ..
     Print("Game over! You survived " .. numTurns .. " turns.\n" ..
           "Do you want to play again (Y/N)?")
           "Do you want to play again (Y/N)?")
-    
+
     gameOn = false
     gameOn = false
 end
 end
 
 
@@ -125,7 +125,7 @@ function Advance()
     elseif urhoThreat == 0 and Random() < 0.2 then
     elseif urhoThreat == 0 and Random() < 0.2 then
         urhoThreat = urhoThreat + 1
         urhoThreat = urhoThreat + 1
     end
     end
-    
+
     if urhoThreat > 0 then
     if urhoThreat > 0 then
         Print(urhoThreatLevels[urhoThreat] .. ".")
         Print(urhoThreatLevels[urhoThreat] .. ".")
     end
     end
@@ -139,9 +139,9 @@ function Advance()
             Print("You are " .. hungerLevels[hunger + 1] .. ".")
             Print("You are " .. hungerLevels[hunger + 1] .. ".")
         end
         end
     end
     end
-    
+
     eatenLastTurn = false
     eatenLastTurn = false
-    
+
     if foodAvailable then
     if foodAvailable then
         Print("The floating pieces of fish food are quickly eaten by other fish in the tank.")
         Print("The floating pieces of fish food are quickly eaten by other fish in the tank.")
         foodAvailable = false
         foodAvailable = false
@@ -149,7 +149,7 @@ function Advance()
         Print("The overhead dispenser drops pieces of delicious fish food to the water!")
         Print("The overhead dispenser drops pieces of delicious fish food to the water!")
         foodAvailable = true
         foodAvailable = true
     end
     end
-    
+
     numTurns = numTurns + 1
     numTurns = numTurns + 1
 end
 end
 
 
@@ -163,7 +163,7 @@ function HandleInput(input)
         Print("Empty input given!")
         Print("Empty input given!")
         return
         return
     end
     end
-    
+
     if inputLower == "quit" or inputLower == "exit" then
     if inputLower == "quit" or inputLower == "exit" then
         engine:Exit()
         engine:Exit()
     elseif gameOn then
     elseif gameOn then
@@ -230,3 +230,16 @@ function Print(input)
     -- Logging appears both in the engine console and stdout
     -- Logging appears both in the engine console and stdout
     Log:WriteRaw(input .. "\n")
     Log:WriteRaw(input .. "\n")
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
+        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 29 - 6
Bin/Data/LuaScripts/27_Urho2DPhysics.lua

@@ -5,9 +5,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -46,7 +43,7 @@ function CreateScene()
 
 
     -- Create 2D physics world component
     -- Create 2D physics world component
     scene_:CreateComponent("PhysicsWorld2D")
     scene_:CreateComponent("PhysicsWorld2D")
-    
+
     local boxSprite = cache:GetResource("Sprite2D", "Urho2D/Box.png")
     local boxSprite = cache:GetResource("Sprite2D", "Urho2D/Box.png")
     local ballSprite = cache:GetResource("Sprite2D", "Urho2D/Ball.png")
     local ballSprite = cache:GetResource("Sprite2D", "Urho2D/Ball.png")
 
 
@@ -54,7 +51,7 @@ function CreateScene()
     local groundNode = scene_:CreateChild("Ground")
     local groundNode = scene_:CreateChild("Ground")
     groundNode.position = Vector3(0.0, -3.0, 0.0)
     groundNode.position = Vector3(0.0, -3.0, 0.0)
     groundNode.scale = Vector3(200.0, 1.0, 0.0)
     groundNode.scale = Vector3(200.0, 1.0, 0.0)
-    
+
     -- Create 2D rigid body for gound
     -- Create 2D rigid body for gound
     local groundBody = groundNode:CreateComponent("RigidBody2D")
     local groundBody = groundNode:CreateComponent("RigidBody2D")
 
 
@@ -67,7 +64,7 @@ function CreateScene()
     groundShape.size = Vector2(0.32, 0.32)
     groundShape.size = Vector2(0.32, 0.32)
     -- Set friction
     -- Set friction
     groundShape.friction = 0.5
     groundShape.friction = 0.5
-    
+
     local NUM_OBJECTS = 100
     local NUM_OBJECTS = 100
     for i = 1, NUM_OBJECTS do
     for i = 1, NUM_OBJECTS do
         local node  = scene_:CreateChild("RigidBody")
         local node  = scene_:CreateChild("RigidBody")
@@ -158,6 +155,9 @@ end
 function SubscribeToEvents()
 function SubscribeToEvents()
     -- Subscribe HandleUpdate() function for processing update events
     -- Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate")
     SubscribeToEvent("Update", "HandleUpdate")
+
+    -- Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 end
 
 
 function HandleUpdate(eventType, eventData)
 function HandleUpdate(eventType, eventData)
@@ -167,3 +167,26 @@ function HandleUpdate(eventType, eventData)
     -- Move the camera, scale movement with time step
     -- Move the camera, scale movement with time step
     MoveCamera(timeStep)
     MoveCamera(timeStep)
 end
 end
+
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
+end

+ 29 - 5
Bin/Data/LuaScripts/28_Urho2DPhysicsRope.lua

@@ -3,10 +3,8 @@
 --     - Create revolute constraint
 --     - Create revolute constraint
 --     - Create roop constraint
 --     - Create roop constraint
 --     - Displaying physics debug geometry
 --     - Displaying physics debug geometry
-require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
+require "LuaScripts/Utilities/Sample"
 
 
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
@@ -43,7 +41,7 @@ function CreateScene()
     local camera = cameraNode:CreateComponent("Camera")
     local camera = cameraNode:CreateComponent("Camera")
     camera.orthographic = true
     camera.orthographic = true
     camera.orthoSize = graphics.height * 0.05
     camera.orthoSize = graphics.height * 0.05
-    
+
     -- Create 2D physics world component
     -- Create 2D physics world component
     local physicsWorld = scene_:CreateComponent("PhysicsWorld2D")
     local physicsWorld = scene_:CreateComponent("PhysicsWorld2D")
     physicsWorld.drawJoint = true
     physicsWorld.drawJoint = true
@@ -157,6 +155,9 @@ end
 function SubscribeToEvents()
 function SubscribeToEvents()
     -- Subscribe HandleUpdate() function for processing update events
     -- Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate")
     SubscribeToEvent("Update", "HandleUpdate")
+
+    -- Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate")
 end
 end
 
 
 function HandleUpdate(eventType, eventData)
 function HandleUpdate(eventType, eventData)
@@ -167,6 +168,29 @@ function HandleUpdate(eventType, eventData)
     MoveCamera(timeStep)
     MoveCamera(timeStep)
 
 
     local physicsWorld = scene_:GetComponent("PhysicsWorld2D")
     local physicsWorld = scene_:GetComponent("PhysicsWorld2D")
-    physicsWorld:DrawDebugGeometry();
+    physicsWorld:DrawDebugGeometry()
+
+end
 
 
+-- Create XML patch instructions for screen joystick layout specific to this sample app
+function GetScreenJoystickPatchString()
+    return
+        "<patch>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" ..
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" ..
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" ..
+        "        <element type=\"Text\">" ..
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" ..
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" ..
+        "        </element>" ..
+        "    </add>" ..
+        "</patch>"
 end
 end

+ 0 - 5
Bin/Data/LuaScripts/30_LightAnimation.lua

@@ -4,11 +4,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()

+ 5 - 10
Bin/Data/LuaScripts/31_MaterialAnimation.lua

@@ -4,11 +4,6 @@
 
 
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
-local scene_ = nil
-local cameraNode = nil
-local yaw = 0.0
-local pitch = 0.0
-
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
@@ -61,11 +56,11 @@ function CreateScene()
     local mushroomMat = cache:GetResource("Material", "Materials/Mushroom.xml")
     local mushroomMat = cache:GetResource("Material", "Materials/Mushroom.xml")
     -- Apply shader parameter animation to material
     -- Apply shader parameter animation to material
     local specColorAnimation = ValueAnimation:new()
     local specColorAnimation = ValueAnimation:new()
-    specColorAnimation:SetKeyFrame(0.0, Variant(Color(0.1, 0.1, 0.1, 16.0)));
-    specColorAnimation:SetKeyFrame(1.0, Variant(Color(1.0, 0.0, 0.0, 2.0)));
-    specColorAnimation:SetKeyFrame(2.0, Variant(Color(1.0, 1.0, 0.0, 2.0)));
-    specColorAnimation:SetKeyFrame(3.0, Variant(Color(0.1, 0.1, 0.1, 16.0)));
-    mushroomMat:SetShaderParameterAnimation("MatSpecColor", specColorAnimation);
+    specColorAnimation:SetKeyFrame(0.0, Variant(Color(0.1, 0.1, 0.1, 16.0)))
+    specColorAnimation:SetKeyFrame(1.0, Variant(Color(1.0, 0.0, 0.0, 2.0)))
+    specColorAnimation:SetKeyFrame(2.0, Variant(Color(1.0, 1.0, 0.0, 2.0)))
+    specColorAnimation:SetKeyFrame(3.0, Variant(Color(0.1, 0.1, 0.1, 16.0)))
+    mushroomMat:SetShaderParameterAnimation("MatSpecColor", specColorAnimation)
 
 
     local NUM_OBJECTS = 200
     local NUM_OBJECTS = 200
     for i = 1, NUM_OBJECTS do
     for i = 1, NUM_OBJECTS do

+ 95 - 13
Bin/Data/LuaScripts/Utilities/Sample.lua

@@ -5,10 +5,29 @@
 --    - Toggle rendering options from the keys 1-8
 --    - Toggle rendering options from the keys 1-8
 --    - Take screenshots with key 9
 --    - Take screenshots with key 9
 --    - Handle Esc key down to hide Console or exit application
 --    - Handle Esc key down to hide Console or exit application
+--    - Init touch input on mobile platform using screen joysticks (patched for each individual sample)
 
 
 local logoSprite = nil
 local logoSprite = nil
+scene_ = nil -- Scene
+screenJoystickIndex = M_MAX_UNSIGNED -- Screen joystick index for navigational controls (mobile platforms only)
+screenJoystickSettingsIndex = M_MAX_UNSIGNED -- Screen joystick index for settings (mobile platforms only)
+touchEnabled = false -- Flag to indicate whether touch input has been enabled
+paused = false -- Pause flag
+drawDebug = false -- Draw debug geometry flag
+cameraNode = nil -- Camera scene node
+yaw = 0 -- Camera yaw angle
+pitch = 0 -- Camera pitch angle
+TOUCH_SENSITIVITY = 2
 
 
 function SampleStart()
 function SampleStart()
+    if GetPlatform() == "Android" or GetPlatform() == "iOS" or input.touchEmulation then
+        -- On mobile platform, enable touch by adding a screen joystick
+        InitTouchInput()
+    elseif input:GetNumJoysticks() == 0 then
+        -- On desktop platform, do not detect touch when we already got a joystick
+        SubscribeToEvent("TouchBegin", "HandleTouchBegin")
+    end
+
     -- Create logo
     -- Create logo
     CreateLogo()
     CreateLogo()
 
 
@@ -20,6 +39,22 @@ function SampleStart()
 
 
     -- Subscribe key down event
     -- Subscribe key down event
     SubscribeToEvent("KeyDown", "HandleKeyDown")
     SubscribeToEvent("KeyDown", "HandleKeyDown")
+
+    -- Subscribe scene update event
+    SubscribeToEvent("SceneUpdate", "HandleSceneUpdate")
+end
+
+function InitTouchInput()
+    touchEnabled = true
+    local layout = cache:GetResource("XMLFile", "UI/ScreenJoystick_Samples.xml")
+    local patchString = GetScreenJoystickPatchString()
+    if patchString ~= "" then
+        -- Patch the screen joystick layout further on demand
+        local patchFile = XMLFile()
+        if patchFile:FromString(patchString) then layout:Patch(patchFile) end
+    end
+    screenJoystickIndex = input:AddScreenJoystick(layout, cache:GetResource("XMLFile", "UI/DefaultStyle.xml"))
+    input:SetScreenJoystickVisible(screenJoystickSettingsIndex, true)
 end
 end
 
 
 function SetLogoVisible(enable)
 function SetLogoVisible(enable)
@@ -34,31 +69,31 @@ function CreateLogo()
     if logoTexture == nil then
     if logoTexture == nil then
         return
         return
     end
     end
-    
+
     -- Create logo sprite and add to the UI layout
     -- Create logo sprite and add to the UI layout
     logoSprite = ui.root:CreateChild("Sprite")
     logoSprite = ui.root:CreateChild("Sprite")
-    
+
     -- Set logo sprite texture
     -- Set logo sprite texture
     logoSprite:SetTexture(logoTexture)
     logoSprite:SetTexture(logoTexture)
-    
+
     local textureWidth = logoTexture.width
     local textureWidth = logoTexture.width
     local textureHeight = logoTexture.height
     local textureHeight = logoTexture.height
-    
+
     -- Set logo sprite scale
     -- Set logo sprite scale
     logoSprite:SetScale(256 / textureWidth)
     logoSprite:SetScale(256 / textureWidth)
-    
+
     -- Set logo sprite size
     -- Set logo sprite size
     logoSprite:SetSize(textureWidth, textureHeight)
     logoSprite:SetSize(textureWidth, textureHeight)
-    
+
     -- Set logo sprite hot spot
     -- Set logo sprite hot spot
     logoSprite.hotSpot = IntVector2(0, textureHeight)
     logoSprite.hotSpot = IntVector2(0, textureHeight)
-    
+
     -- Set logo sprite alignment
     -- Set logo sprite alignment
     logoSprite:SetAlignment(HA_LEFT, VA_BOTTOM);
     logoSprite:SetAlignment(HA_LEFT, VA_BOTTOM);
-    
+
     -- Make logo not fully opaque to show the scene underneath
     -- Make logo not fully opaque to show the scene underneath
     logoSprite.opacity = 0.75
     logoSprite.opacity = 0.75
-    
+
     -- Set a low priority for the logo so that other UI elements can be drawn on top
     -- Set a low priority for the logo so that other UI elements can be drawn on top
     logoSprite.priority = -100
     logoSprite.priority = -100
 end
 end
@@ -75,11 +110,12 @@ function CreateConsoleAndDebugHud()
     if uiStyle == nil then
     if uiStyle == nil then
         return
         return
     end
     end
-    
+
     -- Create console
     -- Create console
     engine:CreateConsole()
     engine:CreateConsole()
     console.defaultStyle = uiStyle
     console.defaultStyle = uiStyle
-    
+    console.background.opacity = 0.8
+
     -- Create debug HUD
     -- Create debug HUD
     engine:CreateDebugHud()
     engine:CreateDebugHud()
     debugHud.defaultStyle = uiStyle
     debugHud.defaultStyle = uiStyle
@@ -103,8 +139,18 @@ function HandleKeyDown(eventType, eventData)
     end
     end
 
 
     if ui.focusElement == nil then
     if ui.focusElement == nil then
+        -- Preferences / Pause
+        if key == KEY_SELECT and touchEnabled then
+            paused = not paused
+            if screenJoystickSettingsIndex == M_MAX_UNSIGNED then
+                -- Lazy initialization
+                screenJoystickSettingsIndex = input:AddScreenJoystick(cache:GetResource("XMLFile", "UI/ScreenJoystickSettings_Samples.xml"), cache:GetResource("XMLFile", "UI/DefaultStyle.xml"))
+            else
+                input:SetScreenJoystickVisible(screenJoystickSettingsIndex, paused)
+            end
+
         -- Texture quality
         -- Texture quality
-        if key == KEY_1 then
+        elseif key == KEY_1 then
             local quality = renderer.textureQuality
             local quality = renderer.textureQuality
             quality = quality + 1
             quality = quality + 1
             if quality > QUALITY_HIGH then
             if quality > QUALITY_HIGH then
@@ -160,7 +206,7 @@ function HandleKeyDown(eventType, eventData)
         -- Instancing
         -- Instancing
         elseif key == KEY_8 then
         elseif key == KEY_8 then
             renderer.dynamicInstancing = not renderer.dynamicInstancing
             renderer.dynamicInstancing = not renderer.dynamicInstancing
-        
+
         -- Take screenshot
         -- Take screenshot
         elseif key == KEY_9 then
         elseif key == KEY_9 then
             local screenshot = Image()
             local screenshot = Image()
@@ -172,3 +218,39 @@ function HandleKeyDown(eventType, eventData)
         end
         end
     end
     end
 end
 end
+
+function HandleSceneUpdate(eventType, eventData)
+    -- Move the camera by touch, if the camera node is initialized by descendant sample class
+    if touchEnabled and cameraNode then
+        for i=0, input:GetNumTouches()-1 do
+            local state = input:GetTouch(i)
+            if not state.touchedElement then -- Touch on empty space
+                if state.delta.x or state.delta.y then
+                    local camera = cameraNode:GetComponent("Camera")
+                    if not camera then return end
+
+                    yaw = yaw + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x
+                    pitch = pitch + TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y
+
+                    -- Construct new orientation for the camera scene node from yaw and pitch; roll is fixed to zero
+                    cameraNode:SetRotation(Quaternion(pitch, yaw, 0))
+                else
+                    -- Move the cursor to the touch position
+                    local cursor = ui:GetCursor()
+                    if cursor and cursor:IsVisible() then cursor:SetPosition(state.position) end
+                end
+            end
+        end
+    end
+end
+
+function HandleTouchBegin(eventType, eventData)
+    -- On some platforms like Windows the presence of touch input can only be detected dynamically
+    InitTouchInput()
+    UnsubscribeFromEvent("TouchBegin")
+end
+
+-- Create empty XML patch instructions for screen joystick layout if none defined
+function GetScreenJoystickPatchString()
+    return ""
+end

+ 17 - 139
Bin/Data/LuaScripts/Utilities/Touch.lua

@@ -1,128 +1,42 @@
 -- Mobile framework for Android/iOS
 -- Mobile framework for Android/iOS
 -- Gamepad from Ninja Snow War
 -- Gamepad from Ninja Snow War
--- Gyroscope (activated by default)
 -- Touches patterns:
 -- Touches patterns:
 --     - 1 finger touch  = pick object through raycast
 --     - 1 finger touch  = pick object through raycast
 --     - 1 or 2 fingers drag  = rotate camera
 --     - 1 or 2 fingers drag  = rotate camera
---     - 3 fingers touch = switch between first/third person view
---     - 4 fingers touch = switch shadows on/off
 --     - 2 fingers sliding in opposite direction (up/down) = zoom in/out
 --     - 2 fingers sliding in opposite direction (up/down) = zoom in/out
 
 
--- 3 fingers touch & 4 fingers touch could be used to switch gyroscope on/off, activate/deactivate secondary viewport, activate a panel GUI, switch debug HUD/geometry, toggle console, switch the gyroscope...
+-- Setup: Call the update function 'UpdateTouches()' from HandleUpdate or equivalent update handler function
 
 
--- Setup:
--- - On init, call this script using 'require "LuaScripts/Utilities/Touch" end' then 'InitTouchInput()' on mobile platforms
---   -> to detect platform, use 'if GetPlatform() == "Android" or GetPlatform() == "iOS"')
--- - Subscribe to touch events (Begin, Move, End) using 'SubscribeToTouchEvents()'
--- - Call the update function 'updateTouches()' from HandleUpdate or equivalent update handler function
-
-TOUCH_SENSITIVITY = 5.0
-GYROSCOPE_THRESHOLD = 0.1
+local GYROSCOPE_THRESHOLD = 0.1
 CAMERA_MIN_DIST = 1.0
 CAMERA_MIN_DIST = 1.0
-CAMERA_INITIAL_DIST = 5.0
 CAMERA_MAX_DIST = 20.0
 CAMERA_MAX_DIST = 20.0
 
 
-local moveTouchID = -1
-local rotateTouchID = -1
-local fireTouchID = -1
-local moveButton
-local fireButton
-local touchButtonSize = 96
-local touchButtonBorder = 12
 local zoom = false
 local zoom = false
-local newFirstPerson = nil
-local shadowMode = true
-
-firstPerson = false
-touchEnabled = false
-cameraDistance = CAMERA_INITIAL_DIST
-cameraNode = nil
-
--- Create Gamepad Buttons
-
-function InitTouchInput()
-    moveButton = ui.root:CreateChild("BorderImage")
-    moveButton.texture = cache:GetResource("Texture2D", "Textures/TouchInput.png")
-    moveButton.imageRect = IntRect(0, 0, 96, 96) -- Crop right side of the texture
-    moveButton:SetAlignment(HA_LEFT, VA_BOTTOM)
-    moveButton:SetPosition(touchButtonBorder, -touchButtonBorder)
-    moveButton:SetSize(touchButtonSize, touchButtonSize)
-    moveButton.opacity = 0.25
+useGyroscope = false
+cameraDistance = 5.0
 
 
-    fireButton = ui.root:CreateChild("BorderImage")
-    fireButton.texture = cache:GetResource("Texture2D", "Textures/TouchInput.png")
-    fireButton.imageRect = IntRect(96, 0, 192, 96) -- Crop left side of the texture
-    fireButton:SetAlignment(HA_RIGHT, VA_BOTTOM)
-    fireButton:SetPosition(-touchButtonBorder, -touchButtonBorder)
-    fireButton:SetSize(touchButtonSize, touchButtonSize)
-    fireButton.opacity = 0.25
-
-    touchEnabled = true
-end
-
-function SubscribeToTouchEvents()
-    SubscribeToEvent("TouchBegin", "HandleTouchBegin")
-    SubscribeToEvent("TouchEnd", "HandleTouchEnd")
-end
+function UpdateTouches(controls) -- Called from HandleUpdate
 
 
-function updateTouches(controls) -- Called from HandleUpdate
-    local controls = characterNode:GetScriptObject().controls
-    local camera = cameraNode:GetComponent("Camera")
     zoom = false -- reset bool
     zoom = false -- reset bool
 
 
-    -- Touch Inputs
-    if touchEnabled then
+    -- Zoom in/out
+    if input.numTouches == 2 then
+        local touch1 = input:GetTouch(0)
+        local touch2 = input:GetTouch(1)
 
 
-        -- Zoom in/out
-        if input.numTouches == 2 then
-            local touch1 = input:GetTouch(0)
-            local touch2 = input:GetTouch(1)
+        -- Check for zoom pattern (touches moving in opposite directions and on empty space)
+        if not touch1.touchedElement and not touch2.touchedElement and ((touch1.delta.y > 0 and touch2.delta.y < 0) or (touch1.delta.y < 0 and touch2.delta.y > 0)) then zoom = true else zoom = false end
 
 
-            -- Check for zoom pattern (touches moving in opposite directions)
-            if (touch1.delta.y > 0 and touch2.delta.y < 0) or (touch1.delta.y < 0 and touch2.delta.y > 0) then zoom = true else zoom = false end
-
-            -- Check for zoom direction (in/out)
-            if zoom then
-                if Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y) then sens = -1 else sens = 1 end
-                cameraDistance = cameraDistance + Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50
-                cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST) -- Restrict zoom range to [1;20]
-            end
+        -- Check for zoom direction (in/out)
+        if zoom then
+            if Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y) then sens = -1 else sens = 1 end
+            cameraDistance = cameraDistance + Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50
+            cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST) -- Restrict zoom range to [1;20]
         end
         end
-
-        -- Switch 1st/3rd person mode
-        if input.numTouches == 3 then newFirstPerson = not firstPerson end
-
-        -- Switch draw debug
-        if input.numTouches == 4 then shadowMode = not renderer.drawShadows end
-
-        -- Rotate and Move
-        if not zoom then
-            for i = 0, input.numTouches-1 do -- numtouches=[0;4]
-
-                local touch = input:GetTouch(i) -- TouchState
-
-                if touch.touchID == rotateTouchID then
-                    controls.yaw = controls.yaw + TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.x
-                    controls.pitch = controls.pitch + TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.y
-                    controls.pitch = Clamp(controls.pitch, -80, 80) -- Limit pitch
-                end
-
-                if touch.touchID == moveTouchID then
-                    local relX = touch.position.x - moveButton.screenPosition.x - touchButtonSize / 2
-                    local relY = touch.position.y - moveButton.screenPosition.y - touchButtonSize / 2
-                        if relY < 0 and Abs(relX * 3 / 2) < Abs(relY) then controls:Set(CTRL_FORWARD, true) end
-                        if relY > 0 and Abs(relX * 3 / 2) < Abs(relY) then controls:Set(CTRL_BACK, true) end
-                        if relX < 0 and Abs(relY * 3 / 2) < Abs(relX) then controls:Set(CTRL_LEFT, true) end
-                        if relX > 0 and Abs(relY * 3 / 2) < Abs(relX) then controls:Set(CTRL_RIGHT, true) end
-                    end
-                end
-        end
-
-        if fireTouchID >= 0 then controls:Set(CTRL_JUMP, true) end
     end
     end
 
 
     -- Gyroscope (emulated by SDL through a virtual joystick)
     -- Gyroscope (emulated by SDL through a virtual joystick)
-    if input.numJoysticks > 0 then -- numJoysticks = 1 on iOS & Android
+    if useGyroscope and input.numJoysticks > 0 then -- numJoysticks = 1 on iOS & Android
         local joystick = input:GetJoystickByIndex(0) -- JoystickState
         local joystick = input:GetJoystickByIndex(0) -- JoystickState
         if joystick.numAxes >= 2 then
         if joystick.numAxes >= 2 then
             if joystick:GetAxisPosition(0) < -GYROSCOPE_THRESHOLD then controls:Set(CTRL_LEFT, true) end
             if joystick:GetAxisPosition(0) < -GYROSCOPE_THRESHOLD then controls:Set(CTRL_LEFT, true) end
@@ -132,39 +46,3 @@ function updateTouches(controls) -- Called from HandleUpdate
         end
         end
     end
     end
 end
 end
-
-function HandleTouchBegin(eventType, eventData)
-
-    local touchID = eventData:GetInt("TouchID") -- Get #touches or dragging value
-
-    local pos = IntVector2(eventData:GetInt("X"), eventData:GetInt("Y")) -- Get touch coordinates
-    local element = ui:GetElementAt(pos, false) -- Get gamepad UIElement touched (if any)
-
-    -- Check for gamepad button touched. If none, rotate
-    if element == moveButton then moveTouchID = touchID
-    elseif element == fireButton then fireTouchID = touchID
-    else rotateTouchID = touchID end
-
-    -- Raycast of RigidBodies (for example to acquire a target)
-    local camera = cameraNode:GetComponent("Camera")
-    local cameraRay = camera:GetScreenRay(eventData:GetInt("X") / graphics.width, eventData:GetInt("Y") / graphics.height)
-    local result = scene_:GetComponent("PhysicsWorld"):RaycastSingle(cameraRay, camera.farClip, 2) -- NB: here we restrict targets to layer 2
-    if result.body ~= nil then print("Physics raycast hit " .. result.body:GetNode().name) end
-
-    -- Raycast of drawable components (for targets with or without physics)
-    local result2 = scene_:GetComponent("Octree"):RaycastSingle(cameraRay, RAY_TRIANGLE, camera.farClip, DRAWABLE_GEOMETRY)
-    if result2.drawable ~= nil then print("Drawable raycast hit " .. result2.drawable:GetNode().name) end
-end
-
-
-function HandleTouchEnd(eventType, eventData)
-    local touchID = eventData:GetInt("TouchID")
-
-    if touchID == moveTouchID then moveTouchID = -1 end
-    if touchID == rotateTouchID then rotateTouchID = -1 end
-    if touchID == fireTouchID then fireTouchID = -1 end
-
-    -- On-release Update
-    firstPerson = newFirstPerson
-    renderer.drawShadows = shadowMode
-end

+ 8 - 1
Bin/Data/Scripts/01_HelloWorld.as

@@ -10,7 +10,7 @@ void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
     SampleStart();
     SampleStart();
-    
+
     // Create "Hello World" Text
     // Create "Hello World" Text
     CreateText();
     CreateText();
 
 
@@ -49,3 +49,10 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Do nothing for now, could be extended to eg. animate the display
     // Do nothing for now, could be extended to eg. animate the display
 }
 }
 
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 9 - 1
Bin/Data/Scripts/02_HelloGUI.as

@@ -32,7 +32,7 @@ void Start()
 
 
     // Create and add some controls to the Window
     // Create and add some controls to the Window
     InitControls();
     InitControls();
-    
+
     // Create a draggable Fish
     // Create a draggable Fish
     CreateDraggableFish();
     CreateDraggableFish();
 }
 }
@@ -176,3 +176,11 @@ void HandleControlClicked(StringHash eventType, VariantMap& eventData)
     // Update the Window's title text
     // Update the Window's title text
     windowTitle.text = "Hello " + name + "!";
     windowTitle.text = "Hello " + name + "!";
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 8 - 0
Bin/Data/Scripts/03_Sprites.as

@@ -106,3 +106,11 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move sprites, scale movement with time step
     // Move sprites, scale movement with time step
     MoveSprites(timeStep);
     MoveSprites(timeStep);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 5 - 6
Bin/Data/Scripts/04_StaticScene.as

@@ -3,14 +3,10 @@
 //     - Creating a 3D scene with static content
 //     - Creating a 3D scene with static content
 //     - Displaying the scene using the Renderer subsystem
 //     - Displaying the scene using the Renderer subsystem
 //     - Handling keyboard and mouse input to move a freelook camera
 //     - Handling keyboard and mouse input to move a freelook camera
+//     - Applying a material shader to animate vegetation (simulate wind)
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -71,7 +67,7 @@ void CreateScene()
         mushroomNode.SetScale(0.5f + Random(2.0f));
         mushroomNode.SetScale(0.5f + Random(2.0f));
         StaticModel@ mushroomObject = mushroomNode.CreateComponent("StaticModel");
         StaticModel@ mushroomObject = mushroomNode.CreateComponent("StaticModel");
         mushroomObject.model = cache.GetResource("Model", "Models/Mushroom.mdl");
         mushroomObject.model = cache.GetResource("Model", "Models/Mushroom.mdl");
-        mushroomObject.material = cache.GetResource("Material", "Materials/Mushroom.xml");
+        mushroomObject.material = cache.GetResource("Material", "Materials/MushroomWind.xml"); // Apply Vegetation Windy shader
     }
     }
 
 
     // Create a scene node for the camera, which we will move around
     // Create a scene node for the camera, which we will move around
@@ -151,3 +147,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move the camera, scale movement with time step
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
     MoveCamera(timeStep);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 3 - 5
Bin/Data/Scripts/05_AnimatingScene.as

@@ -6,11 +6,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -164,3 +159,6 @@ class Rotator : ScriptObject
         node.Rotate(Quaternion(rotationSpeed.x * timeStep, rotationSpeed.y * timeStep, rotationSpeed.z * timeStep));
         node.Rotate(Quaternion(rotationSpeed.x * timeStep, rotationSpeed.y * timeStep, rotationSpeed.z * timeStep));
     }
     }
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 22 - 15
Bin/Data/Scripts/06_SkeletalAnimation.as

@@ -8,12 +8,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -21,10 +15,10 @@ void Start()
 
 
     // Create the scene content
     // Create the scene content
     CreateScene();
     CreateScene();
-    
+
     // Create the UI content
     // Create the UI content
     CreateInstructions();
     CreateInstructions();
-    
+
     // Setup the viewport for displaying the scene
     // Setup the viewport for displaying the scene
     SetupViewport();
     SetupViewport();
 
 
@@ -35,7 +29,7 @@ void Start()
 void CreateScene()
 void CreateScene()
 {
 {
     scene_ = Scene();
     scene_ = Scene();
-    
+
     // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     // Also create a DebugRenderer component so that we can draw debug geometry
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("Octree");
@@ -47,7 +41,7 @@ void CreateScene()
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
-    
+
     // Create a Zone component for ambient lighting & fog control
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -56,7 +50,7 @@ void CreateScene()
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogStart = 100.0f;
     zone.fogStart = 100.0f;
     zone.fogEnd = 300.0f;
     zone.fogEnd = 300.0f;
-    
+
     // Create a directional light to the world. Enable cascaded shadows on it
     // Create a directional light to the world. Enable cascaded shadows on it
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
@@ -66,7 +60,7 @@ void CreateScene()
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
-    
+
     // Create animated models
     // Create animated models
     const uint NUM_MODELS = 100;
     const uint NUM_MODELS = 100;
     const float MODEL_MOVE_SPEED = 2.0f;
     const float MODEL_MOVE_SPEED = 2.0f;
@@ -103,7 +97,7 @@ void CreateScene()
     cameraNode = scene_.CreateChild("Camera");
     cameraNode = scene_.CreateChild("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     camera.farClip = 300.0f;
     camera.farClip = 300.0f;
-    
+
     // Set an initial position for the camera scene node above the plane
     // Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
 }
 }
@@ -112,7 +106,7 @@ void CreateInstructions()
 {
 {
     // Construct new Text object, set string to display and font to use
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
     Text@ instructionText = ui.root.CreateChild("Text");
-    instructionText.text = 
+    instructionText.text =
         "Use WASD keys and mouse to move\n"
         "Use WASD keys and mouse to move\n"
         "Space to toggle debug geometry";
         "Space to toggle debug geometry";
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
@@ -136,7 +130,7 @@ void SubscribeToEvents()
 {
 {
     // Subscribe HandleUpdate() function for processing update events
     // Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate");
     SubscribeToEvent("Update", "HandleUpdate");
-    
+
     // Subscribe HandlePostRenderUpdate() function for processing the post-render update event, sent after Renderer subsystem is
     // Subscribe HandlePostRenderUpdate() function for processing the post-render update event, sent after Renderer subsystem is
     // done with defining the draw calls for the viewports (but before actually executing them.) We will request debug geometry
     // done with defining the draw calls for the viewports (but before actually executing them.) We will request debug geometry
     // rendering during that event
     // rendering during that event
@@ -226,3 +220,16 @@ class Mover : ScriptObject
             state.AddTime(timeStep);
             state.AddTime(timeStep);
     }
     }
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+    "<patch>"+
+    "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />"+
+    "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>"+
+    "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">"+
+    "        <element type=\"Text\">"+
+    "            <attribute name=\"Name\" value=\"KeyBinding\" />"+
+    "            <attribute name=\"Text\" value=\"SPACE\" />"+
+    "        </element>"+
+    "    </add>"+
+    "</patch>";

+ 17 - 10
Bin/Data/Scripts/07_Billboards.as

@@ -6,12 +6,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -19,10 +13,10 @@ void Start()
 
 
     // Create the scene content
     // Create the scene content
     CreateScene();
     CreateScene();
-    
+
     // Create the UI content
     // Create the UI content
     CreateInstructions();
     CreateInstructions();
-    
+
     // Setup the viewport for displaying the scene
     // Setup the viewport for displaying the scene
     SetupViewport();
     SetupViewport();
 
 
@@ -33,7 +27,7 @@ void Start()
 void CreateScene()
 void CreateScene()
 {
 {
     scene_ = Scene();
     scene_ = Scene();
-    
+
     // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     // Create octree, use default volume (-1000, -1000, -1000) to (1000, 1000, 1000)
     // Also create a DebugRenderer component so that we can draw debug geometry
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("Octree");
@@ -168,7 +162,7 @@ void CreateInstructions()
 {
 {
     // Construct new Text object, set string to display and font to use
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
     Text@ instructionText = ui.root.CreateChild("Text");
-    instructionText.text = 
+    instructionText.text =
         "Use WASD keys and mouse to move\n"
         "Use WASD keys and mouse to move\n"
         "Space to toggle debug geometry";
         "Space to toggle debug geometry";
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
@@ -278,3 +272,16 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
     if (drawDebug)
         renderer.DrawDebugGeometry(true);
         renderer.DrawDebugGeometry(true);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 29 - 14
Bin/Data/Scripts/08_Decals.as

@@ -7,12 +7,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -20,10 +14,10 @@ void Start()
 
 
     // Create the scene content
     // Create the scene content
     CreateScene();
     CreateScene();
-    
+
     // Create the UI content
     // Create the UI content
     CreateUI();
     CreateUI();
-    
+
     // Setup the viewport for displaying the scene
     // Setup the viewport for displaying the scene
     SetupViewport();
     SetupViewport();
 
 
@@ -39,14 +33,14 @@ void CreateScene()
     // Also create a DebugRenderer component so that we can draw debug geometry
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("DebugRenderer");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create scene node & StaticModel component for showing a static plane
     // Create scene node & StaticModel component for showing a static plane
     Node@ planeNode = scene_.CreateChild("Plane");
     Node@ planeNode = scene_.CreateChild("Plane");
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
-    
+
     // Create a Zone component for ambient lighting & fog control
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -96,7 +90,7 @@ void CreateScene()
         if (size >= 3.0f)
         if (size >= 3.0f)
             boxObject.occluder = true;
             boxObject.occluder = true;
     }
     }
-    
+
     // Create the camera. Limit far clip distance to match the fog
     // Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_.CreateChild("Camera");
     cameraNode = scene_.CreateChild("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
@@ -116,7 +110,7 @@ void CreateUI()
     ui.cursor = cursor;
     ui.cursor = cursor;
     // Set starting position of the cursor at the rendering window center
     // Set starting position of the cursor at the rendering window center
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
-    
+
     // Construct new Text object, set string to display and font to use
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
     Text@ instructionText = ui.root.CreateChild("Text");
     instructionText.text =
     instructionText.text =
@@ -159,12 +153,12 @@ void MoveCamera(float timeStep)
     // Do not move if the UI has a focused element (the console)
     // Do not move if the UI has a focused element (the console)
     if (ui.focusElement !is null)
     if (ui.focusElement !is null)
         return;
         return;
-    
+
     // Movement speed as world units per second
     // Movement speed as world units per second
     const float MOVE_SPEED = 20.0f;
     const float MOVE_SPEED = 20.0f;
     // Mouse sensitivity as degrees per pixel
     // Mouse sensitivity as degrees per pixel
     const float MOUSE_SENSITIVITY = 0.1f;
     const float MOUSE_SENSITIVITY = 0.1f;
-    
+
     // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
     // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
     // Only move the camera when the cursor is hidden
     // Only move the camera when the cursor is hidden
     if (!ui.cursor.visible)
     if (!ui.cursor.visible)
@@ -259,3 +253,24 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
     if (drawDebug)
         renderer.DrawDebugGeometry(false);
         renderer.DrawDebugGeometry(false);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Paint</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 50 - 10
Bin/Data/Scripts/09_MultipleViewports.as

@@ -5,12 +5,7 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
 Node@ rearCameraNode;
 Node@ rearCameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
 
 
 void Start()
 void Start()
 {
 {
@@ -19,10 +14,10 @@ void Start()
 
 
     // Create the scene content
     // Create the scene content
     CreateScene();
     CreateScene();
-    
+
     // Create the UI content
     // Create the UI content
     CreateInstructions();
     CreateInstructions();
-    
+
     // Setup the viewports for displaying the scene
     // Setup the viewports for displaying the scene
     SetupViewports();
     SetupViewports();
 
 
@@ -38,14 +33,14 @@ void CreateScene()
     // Also create a DebugRenderer component so that we can draw debug geometry
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("DebugRenderer");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create scene node & StaticModel component for showing a static plane
     // Create scene node & StaticModel component for showing a static plane
     Node@ planeNode = scene_.CreateChild("Plane");
     Node@ planeNode = scene_.CreateChild("Plane");
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
-    
+
     // Create a Zone component for ambient lighting & fog control
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -141,7 +136,7 @@ void SetupViewports()
     // Set up the front camera viewport
     // Set up the front camera viewport
     Viewport@ viewport = Viewport(scene_, cameraNode.GetComponent("Camera"));
     Viewport@ viewport = Viewport(scene_, cameraNode.GetComponent("Camera"));
     renderer.viewports[0] = viewport;
     renderer.viewports[0] = viewport;
-    
+
     // Clone the default render path so that we do not interfere with the other viewport, then add
     // Clone the default render path so that we do not interfere with the other viewport, then add
     // bloom and FXAA post process effects to the front viewport. Render path commands can be tagged
     // bloom and FXAA post process effects to the front viewport. Render path commands can be tagged
     // for example with the effect name to allow easy toggling on and off. We start with the effects
     // for example with the effect name to allow easy toggling on and off. We start with the effects
@@ -229,3 +224,48 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
     if (drawDebug)
         renderer.DrawDebugGeometry(false);
         renderer.DrawDebugGeometry(false);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element\">" +
+        "        <element type=\"Button\">" +
+        "            <attribute name=\"Name\" value=\"Button3\" />" +
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" +
+        "            <attribute name=\"Size\" value=\"96 96\" />" +
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" +
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" +
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" +
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" +
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" +
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"Label\" />" +
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" +
+        "                <attribute name=\"Text\" value=\"FXAA\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "                <attribute name=\"Text\" value=\"F\" />" +
+        "            </element>" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Bloom</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"B\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 5 - 6
Bin/Data/Scripts/10_RenderToTexture.as

@@ -5,12 +5,8 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
 Scene@ rttScene_;
 Scene@ rttScene_;
-Node@ cameraNode;
 Node@ rttCameraNode;
 Node@ rttCameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
 
 
 void Start()
 void Start()
 {
 {
@@ -77,7 +73,7 @@ void CreateScene()
         light.lightType = LIGHT_POINT;
         light.lightType = LIGHT_POINT;
         light.range = 30.0f;
         light.range = 30.0f;
     }
     }
-    
+
     {
     {
         // Create the scene in which we move around
         // Create the scene in which we move around
         scene_ = Scene();
         scene_ = Scene();
@@ -224,7 +220,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
     // Take the frame time step, which is stored as a float
     // Take the frame time step, which is stored as a float
     float timeStep = eventData["TimeStep"].GetFloat();
     float timeStep = eventData["TimeStep"].GetFloat();
-    
+
     // Move the camera, scale movement with time step
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
     MoveCamera(timeStep);
 }
 }
@@ -240,3 +236,6 @@ class Rotator : ScriptObject
         node.Rotate(Quaternion(rotationSpeed.x * timeStep, rotationSpeed.y * timeStep, rotationSpeed.z * timeStep));
         node.Rotate(Quaternion(rotationSpeed.x * timeStep, rotationSpeed.y * timeStep, rotationSpeed.z * timeStep));
     }
     }
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 27 - 12
Bin/Data/Scripts/11_Physics.as

@@ -7,12 +7,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -42,7 +36,7 @@ void CreateScene()
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("DebugRenderer");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create a Zone component for ambient lighting & fog control
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -61,7 +55,7 @@ void CreateScene()
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
-    
+
     // Create skybox. The Skybox component is used like StaticModel, but it will be always located at the camera, giving the
     // Create skybox. The Skybox component is used like StaticModel, but it will be always located at the camera, giving the
     // illusion of the box planes being far away. Use just the ordinary Box model and a suitable material, whose shader will
     // illusion of the box planes being far away. Use just the ordinary Box model and a suitable material, whose shader will
     // generate the necessary 3D texture coordinates for cube mapping
     // generate the necessary 3D texture coordinates for cube mapping
@@ -89,7 +83,7 @@ void CreateScene()
         // rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
         // rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
         shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
         shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
     }
     }
-    
+
     {
     {
         // Create a pyramid of movable physics objects
         // Create a pyramid of movable physics objects
         for (int y = 0; y < 8; ++y)
         for (int y = 0; y < 8; ++y)
@@ -102,9 +96,9 @@ void CreateScene()
                 boxObject.model = cache.GetResource("Model", "Models/Box.mdl");
                 boxObject.model = cache.GetResource("Model", "Models/Box.mdl");
                 boxObject.material = cache.GetResource("Material", "Materials/StoneEnvMapSmall.xml");
                 boxObject.material = cache.GetResource("Material", "Materials/StoneEnvMapSmall.xml");
                 boxObject.castShadows = true;
                 boxObject.castShadows = true;
-                
+
                 // Create RigidBody and CollisionShape components like above. Give the RigidBody mass to make it movable
                 // Create RigidBody and CollisionShape components like above. Give the RigidBody mass to make it movable
-                // and also adjust friction. The actual mass is not important; only the mass ratios between colliding 
+                // and also adjust friction. The actual mass is not important; only the mass ratios between colliding
                 // objects are significant
                 // objects are significant
                 RigidBody@ body = boxNode.CreateComponent("RigidBody");
                 RigidBody@ body = boxNode.CreateComponent("RigidBody");
                 body.mass = 1.0f;
                 body.mass = 1.0f;
@@ -114,7 +108,7 @@ void CreateScene()
             }
             }
         }
         }
     }
     }
-    
+
     // Create the camera. Set far clip to match the fog. Note: now we actually create the camera node outside
     // Create the camera. Set far clip to match the fog. Note: now we actually create the camera node outside
     // the scene, because we want it to be unaffected by scene load / save
     // the scene, because we want it to be unaffected by scene load / save
     cameraNode = Node();
     cameraNode = Node();
@@ -255,3 +249,24 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
     if (drawDebug)
         scene_.physicsWorld.DrawDebugGeometry(true);
         scene_.physicsWorld.DrawDebugGeometry(true);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 25 - 10
Bin/Data/Scripts/12_PhysicsStressTest.as

@@ -6,12 +6,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -41,7 +35,7 @@ void CreateScene()
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("DebugRenderer");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create a Zone component for ambient lighting & fog control
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -50,7 +44,7 @@ void CreateScene()
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogStart = 100.0f;
     zone.fogStart = 100.0f;
     zone.fogEnd = 300.0f;
     zone.fogEnd = 300.0f;
-    
+
     // Create a directional light to the world. Enable cascaded shadows on it
     // Create a directional light to the world. Enable cascaded shadows on it
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
@@ -98,7 +92,7 @@ void CreateScene()
             shape.SetTriangleMesh(mushroomObject.model);
             shape.SetTriangleMesh(mushroomObject.model);
         }
         }
     }
     }
-    
+
     {
     {
         // Create a large amount of falling physics objects
         // Create a large amount of falling physics objects
         const uint NUM_OBJECTS = 1000;
         const uint NUM_OBJECTS = 1000;
@@ -121,7 +115,7 @@ void CreateScene()
             shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
             shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
         }
         }
     }
     }
-    
+
     // Create the camera. Limit far clip distance to match the fog. Note: now we actually create the camera node outside
     // Create the camera. Limit far clip distance to match the fog. Note: now we actually create the camera node outside
     // the scene, because we want it to be unaffected by scene load / save
     // the scene, because we want it to be unaffected by scene load / save
     cameraNode = Node();
     cameraNode = Node();
@@ -260,3 +254,24 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     if (drawDebug)
     if (drawDebug)
         scene_.physicsWorld.DrawDebugGeometry(true);
         scene_.physicsWorld.DrawDebugGeometry(true);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 43 - 28
Bin/Data/Scripts/13_Ragdolls.as

@@ -6,12 +6,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -41,7 +35,7 @@ void CreateScene()
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("PhysicsWorld");
     scene_.CreateComponent("DebugRenderer");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create a Zone component for ambient lighting & fog control
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -50,7 +44,7 @@ void CreateScene()
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogColor = Color(0.5f, 0.5f, 0.7f);
     zone.fogStart = 100.0f;
     zone.fogStart = 100.0f;
     zone.fogEnd = 300.0f;
     zone.fogEnd = 300.0f;
-    
+
     // Create a directional light to the world. Enable cascaded shadows on it
     // Create a directional light to the world. Enable cascaded shadows on it
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     Node@ lightNode = scene_.CreateChild("DirectionalLight");
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
     lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
@@ -60,7 +54,7 @@ void CreateScene()
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     light.shadowBias = BiasParameters(0.00025f, 0.5f);
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     // Set cascade splits at 10, 50 and 200 world units, fade shadows out at 80% of maximum shadow distance
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
     light.shadowCascade = CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
-    
+
     {
     {
         // Create a floor object, 500 x 500 world units. Adjust position so that the ground is at zero Y
         // Create a floor object, 500 x 500 world units. Adjust position so that the ground is at zero Y
         Node@ floorNode = scene_.CreateChild("Floor");
         Node@ floorNode = scene_.CreateChild("Floor");
@@ -80,7 +74,7 @@ void CreateScene()
         // rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
         // rendering and physics representation sizes should match (the box model is also 1 x 1 x 1.)
         shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
         shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
     }
     }
-    
+
     // Create animated models
     // Create animated models
     for (int z = -1; z <= 1; ++z)
     for (int z = -1; z <= 1; ++z)
     {
     {
@@ -107,7 +101,7 @@ void CreateScene()
             // Create the capsule shape with an offset so that it is correctly aligned with the model, which
             // Create the capsule shape with an offset so that it is correctly aligned with the model, which
             // has its origin at the feet
             // has its origin at the feet
             shape.SetCapsule(0.7f, 2.0f, Vector3(0.0f, 1.0f, 0.0f));
             shape.SetCapsule(0.7f, 2.0f, Vector3(0.0f, 1.0f, 0.0f));
-            
+
             // Create a custom script object that reacts to collisions and creates the ragdoll
             // Create a custom script object that reacts to collisions and creates the ragdoll
             modelNode.CreateScriptObject(scriptFile, "CreateRagdoll");
             modelNode.CreateScriptObject(scriptFile, "CreateRagdoll");
         }
         }
@@ -226,9 +220,9 @@ void SpawnObject()
     body.rollingFriction = 0.15f;
     body.rollingFriction = 0.15f;
     CollisionShape@ shape = boxNode.CreateComponent("CollisionShape");
     CollisionShape@ shape = boxNode.CreateComponent("CollisionShape");
     shape.SetSphere(1.0f);
     shape.SetSphere(1.0f);
-    
+
     const float OBJECT_VELOCITY = 10.0f;
     const float OBJECT_VELOCITY = 10.0f;
-    
+
     // Set initial velocity for the RigidBody based on camera forward vector. Add also a slight up component
     // Set initial velocity for the RigidBody based on camera forward vector. Add also a slight up component
     // to overcome gravity better
     // to overcome gravity better
     body.linearVelocity = cameraNode.rotation * Vector3(0.0f, 0.25f, 1.0f) * OBJECT_VELOCITY;
     body.linearVelocity = cameraNode.rotation * Vector3(0.0f, 0.25f, 1.0f) * OBJECT_VELOCITY;
@@ -269,7 +263,7 @@ class CreateRagdoll : ScriptObject
             // We do not need the physics components in the AnimatedModel's root scene node anymore
             // We do not need the physics components in the AnimatedModel's root scene node anymore
             node.RemoveComponent("RigidBody");
             node.RemoveComponent("RigidBody");
             node.RemoveComponent("CollisionShape");
             node.RemoveComponent("CollisionShape");
-            
+
             // Create RigidBody & CollisionShape components to bones
             // Create RigidBody & CollisionShape components to bones
             CreateRagdollBone("Bip01_Pelvis", SHAPE_BOX, Vector3(0.3f, 0.2f, 0.25f), Vector3(0.0f, 0.0f, 0.0f),
             CreateRagdollBone("Bip01_Pelvis", SHAPE_BOX, Vector3(0.3f, 0.2f, 0.25f), Vector3(0.0f, 0.0f, 0.0f),
                 Quaternion(0.0f, 0.0f, 0.0f));
                 Quaternion(0.0f, 0.0f, 0.0f));
@@ -297,23 +291,23 @@ class CreateRagdoll : ScriptObject
             // Create Constraints between bones
             // Create Constraints between bones
             CreateRagdollConstraint("Bip01_L_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0.0f, 0.0f, -1.0f),
             CreateRagdollConstraint("Bip01_L_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f));
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_R_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_R_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f));
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_L_Calf", "Bip01_L_Thigh", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_L_Calf", "Bip01_L_Thigh", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_R_Calf", "Bip01_R_Thigh", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_R_Calf", "Bip01_R_Thigh", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_Spine1", "Bip01_Pelvis", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, 1.0f), 
+            CreateRagdollConstraint("Bip01_Spine1", "Bip01_Pelvis", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, 1.0f),
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 0.0f), Vector2(-10.0f, 0.0f));
                 Vector3(0.0f, 0.0f, 1.0f), Vector2(45.0f, 0.0f), Vector2(-10.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_Head", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(-1.0f, 0.0f, 0.0f), 
+            CreateRagdollConstraint("Bip01_Head", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(-1.0f, 0.0f, 0.0f),
                 Vector3(-1.0f, 0.0f, 0.0f), Vector2(0.0f, 30.0f), Vector2(0.0f, 0.0f));
                 Vector3(-1.0f, 0.0f, 0.0f), Vector2(0.0f, 30.0f), Vector2(0.0f, 0.0f));
             CreateRagdollConstraint("Bip01_L_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0.0f, -1.0f, 0.0f),
             CreateRagdollConstraint("Bip01_L_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0.0f, -1.0f, 0.0f),
                 Vector3(0.0f, 1.0f, 0.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f), false);
                 Vector3(0.0f, 1.0f, 0.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f), false);
-            CreateRagdollConstraint("Bip01_R_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0.0f, -1.0f, 0.0f), 
+            CreateRagdollConstraint("Bip01_R_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3(0.0f, -1.0f, 0.0f),
                 Vector3(0.0f, 1.0f, 0.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f), false);
                 Vector3(0.0f, 1.0f, 0.0f), Vector2(45.0f, 45.0f), Vector2(0.0f, 0.0f), false);
-            CreateRagdollConstraint("Bip01_L_Forearm", "Bip01_L_UpperArm", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_L_Forearm", "Bip01_L_UpperArm", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
-            CreateRagdollConstraint("Bip01_R_Forearm", "Bip01_R_UpperArm", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f), 
+            CreateRagdollConstraint("Bip01_R_Forearm", "Bip01_R_UpperArm", CONSTRAINT_HINGE, Vector3(0.0f, 0.0f, -1.0f),
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
                 Vector3(0.0f, 0.0f, -1.0f), Vector2(90.0f, 0.0f), Vector2(0.0f, 0.0f));
 
 
             // Disable keyframe animation from all bones so that they will not interfere with the ragdoll
             // Disable keyframe animation from all bones so that they will not interfere with the ragdoll
@@ -321,13 +315,13 @@ class CreateRagdoll : ScriptObject
             Skeleton@ skeleton = model.skeleton;
             Skeleton@ skeleton = model.skeleton;
             for (uint i = 0; i < skeleton.numBones; ++i)
             for (uint i = 0; i < skeleton.numBones; ++i)
                 skeleton.bones[i].animated = false;
                 skeleton.bones[i].animated = false;
-            
+
             // Finally remove self (the ScriptInstance which holds this script object) from the scene node. Note that this must
             // Finally remove self (the ScriptInstance which holds this script object) from the scene node. Note that this must
             // be the last operation performed in the function
             // be the last operation performed in the function
             self.Remove();
             self.Remove();
         }
         }
     }
     }
-    
+
     void CreateRagdollBone(const String&in boneName, ShapeType type, const Vector3&in size, const Vector3&in position,
     void CreateRagdollBone(const String&in boneName, ShapeType type, const Vector3&in size, const Vector3&in position,
         const Quaternion&in rotation)
         const Quaternion&in rotation)
     {
     {
@@ -338,7 +332,7 @@ class CreateRagdoll : ScriptObject
             log.Warning("Could not find bone " + boneName + " for creating ragdoll physics components");
             log.Warning("Could not find bone " + boneName + " for creating ragdoll physics components");
             return;
             return;
         }
         }
-        
+
         RigidBody@ body = boneNode.CreateComponent("RigidBody");
         RigidBody@ body = boneNode.CreateComponent("RigidBody");
         // Set mass to make movable
         // Set mass to make movable
         body.mass = 1.0f;
         body.mass = 1.0f;
@@ -348,7 +342,7 @@ class CreateRagdoll : ScriptObject
         // Set rest thresholds to ensure the ragdoll rigid bodies come to rest to not consume CPU endlessly
         // Set rest thresholds to ensure the ragdoll rigid bodies come to rest to not consume CPU endlessly
         body.linearRestThreshold = 1.5f;
         body.linearRestThreshold = 1.5f;
         body.angularRestThreshold = 2.5f;
         body.angularRestThreshold = 2.5f;
-    
+
         CollisionShape@ shape = boneNode.CreateComponent("CollisionShape");
         CollisionShape@ shape = boneNode.CreateComponent("CollisionShape");
         // We use either a box or a capsule shape for all of the bones
         // We use either a box or a capsule shape for all of the bones
         if (type == SHAPE_BOX)
         if (type == SHAPE_BOX)
@@ -356,7 +350,7 @@ class CreateRagdoll : ScriptObject
         else
         else
             shape.SetCapsule(size.x, size.y, position, rotation);
             shape.SetCapsule(size.x, size.y, position, rotation);
     }
     }
-    
+
     void CreateRagdollConstraint(const String&in boneName, const String&in parentName, ConstraintType type,
     void CreateRagdollConstraint(const String&in boneName, const String&in parentName, ConstraintType type,
         const Vector3&in axis, const Vector3&in parentAxis, const Vector2&in highLimit, const Vector2&in lowLimit,
         const Vector3&in axis, const Vector3&in parentAxis, const Vector2&in highLimit, const Vector2&in lowLimit,
         bool disableCollision = true)
         bool disableCollision = true)
@@ -373,7 +367,7 @@ class CreateRagdoll : ScriptObject
             log.Warning("Could not find bone " + parentName + " for creating ragdoll constraint");
             log.Warning("Could not find bone " + parentName + " for creating ragdoll constraint");
             return;
             return;
         }
         }
-        
+
         Constraint@ constraint = boneNode.CreateComponent("Constraint");
         Constraint@ constraint = boneNode.CreateComponent("Constraint");
         constraint.constraintType = type;
         constraint.constraintType = type;
         // Most of the constraints in the ragdoll will work better when the connected bodies don't collide against each other
         // Most of the constraints in the ragdoll will work better when the connected bodies don't collide against each other
@@ -389,3 +383,24 @@ class CreateRagdoll : ScriptObject
         constraint.lowLimit = lowLimit;
         constraint.lowLimit = lowLimit;
     }
     }
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Spawn</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 22 - 14
Bin/Data/Scripts/14_SoundEffects.as

@@ -5,8 +5,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-
 Array<String> soundNames = {
 Array<String> soundNames = {
     "Fist",
     "Fist",
     "Explosion",
     "Explosion",
@@ -23,7 +21,7 @@ void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
     SampleStart();
     SampleStart();
-    
+
     // Enable OS cursor
     // Enable OS cursor
     input.mouseVisible = true;
     input.mouseVisible = true;
 
 
@@ -39,7 +37,7 @@ void CreateUI()
     XMLFile@ uiStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
     XMLFile@ uiStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
     // Set style to the UI root so that elements will inherit it
     // Set style to the UI root so that elements will inherit it
     ui.root.defaultStyle = uiStyle;
     ui.root.defaultStyle = uiStyle;
-    
+
     // Create buttons for playing back sounds
     // Create buttons for playing back sounds
     for (uint i = 0; i < soundNames.length; ++i)
     for (uint i = 0; i < soundNames.length; ++i)
     {
     {
@@ -48,11 +46,11 @@ void CreateUI()
         button.vars["SoundResource"] = soundResourceNames[i];
         button.vars["SoundResource"] = soundResourceNames[i];
         SubscribeToEvent(button, "Pressed", "HandlePlaySound");
         SubscribeToEvent(button, "Pressed", "HandlePlaySound");
     }
     }
-    
+
     // Create buttons for playing/stopping music
     // Create buttons for playing/stopping music
     Button@ button = CreateButton(20, 80, 120, 40, "Play Music");
     Button@ button = CreateButton(20, 80, 120, 40, "Play Music");
     SubscribeToEvent(button, "Released", "HandlePlayMusic");
     SubscribeToEvent(button, "Released", "HandlePlayMusic");
-    
+
     button = CreateButton(160, 80, 120, 40, "Stop Music");
     button = CreateButton(160, 80, 120, 40, "Stop Music");
     SubscribeToEvent(button, "Released", "HandleStopMusic");
     SubscribeToEvent(button, "Released", "HandleStopMusic");
 
 
@@ -60,7 +58,7 @@ void CreateUI()
     Slider@ slider = CreateSlider(20, 140, 200, 20, "Sound Volume");
     Slider@ slider = CreateSlider(20, 140, 200, 20, "Sound Volume");
     slider.value = audio.masterGain[SOUND_EFFECT];
     slider.value = audio.masterGain[SOUND_EFFECT];
     SubscribeToEvent(slider, "SliderChanged", "HandleSoundVolume");
     SubscribeToEvent(slider, "SliderChanged", "HandleSoundVolume");
-    
+
     slider = CreateSlider(20, 200, 200, 20, "Music Volume");
     slider = CreateSlider(20, 200, 200, 20, "Music Volume");
     slider.value = audio.masterGain[SOUND_MUSIC];
     slider.value = audio.masterGain[SOUND_MUSIC];
     SubscribeToEvent(slider, "SliderChanged", "HandleMusicVolume");
     SubscribeToEvent(slider, "SliderChanged", "HandleMusicVolume");
@@ -69,18 +67,18 @@ void CreateUI()
 Button@ CreateButton(int x, int y, int xSize, int ySize, const String&in text)
 Button@ CreateButton(int x, int y, int xSize, int ySize, const String&in text)
 {
 {
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
-    
+
     // Create the button and center the text onto it
     // Create the button and center the text onto it
     Button@ button = ui.root.CreateChild("Button");
     Button@ button = ui.root.CreateChild("Button");
     button.SetStyleAuto();
     button.SetStyleAuto();
     button.SetPosition(x, y);
     button.SetPosition(x, y);
     button.SetSize(xSize, ySize);
     button.SetSize(xSize, ySize);
-    
+
     Text@ buttonText = button.CreateChild("Text");
     Text@ buttonText = button.CreateChild("Text");
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.SetFont(font, 12);
     buttonText.SetFont(font, 12);
     buttonText.text = text;
     buttonText.text = text;
-    
+
     return button;
     return button;
 }
 }
 
 
@@ -93,14 +91,14 @@ Slider@ CreateSlider(int x, int y, int xSize, int ySize, const String& text)
     sliderText.SetPosition(x, y);
     sliderText.SetPosition(x, y);
     sliderText.SetFont(font, 12);
     sliderText.SetFont(font, 12);
     sliderText.text = text;
     sliderText.text = text;
-    
+
     Slider@ slider = ui.root.CreateChild("Slider");
     Slider@ slider = ui.root.CreateChild("Slider");
     slider.SetStyleAuto();
     slider.SetStyleAuto();
     slider.SetPosition(x, y + 20);
     slider.SetPosition(x, y + 20);
     slider.SetSize(xSize, ySize);
     slider.SetSize(xSize, ySize);
     // Use 0-1 range for controlling sound/music master volume
     // Use 0-1 range for controlling sound/music master volume
     slider.range = 1.0f;
     slider.range = 1.0f;
-    
+
     return slider;
     return slider;
 }
 }
 
 
@@ -108,7 +106,7 @@ void HandlePlaySound(StringHash eventType, VariantMap& eventData)
 {
 {
     Button@ button = GetEventSender();
     Button@ button = GetEventSender();
     String soundResourceName = button.vars["SoundResource"].GetString();
     String soundResourceName = button.vars["SoundResource"].GetString();
-    
+
     // Get the sound resource
     // Get the sound resource
     Sound@ sound = cache.GetResource("Sound", soundResourceName);
     Sound@ sound = cache.GetResource("Sound", soundResourceName);
 
 
@@ -136,7 +134,7 @@ void HandlePlayMusic(StringHash eventType, VariantMap& eventData)
     Sound@ music = cache.GetResource("Sound", "Music/Ninja Gods.ogg");
     Sound@ music = cache.GetResource("Sound", "Music/Ninja Gods.ogg");
     // Set the song to loop
     // Set the song to loop
     music.looped = true;
     music.looped = true;
-    
+
     // Create a scene node and a sound source for the music
     // Create a scene node and a sound source for the music
     Node@ musicNode = scene_.CreateChild("Music");
     Node@ musicNode = scene_.CreateChild("Music");
     SoundSource@ musicSource = musicNode.CreateComponent("SoundSource");
     SoundSource@ musicSource = musicNode.CreateComponent("SoundSource");
@@ -163,3 +161,13 @@ void HandleMusicVolume(StringHash eventType, VariantMap& eventData)
     audio.masterGain[SOUND_MUSIC] = newVolume;
     audio.masterGain[SOUND_MUSIC] = newVolume;
 }
 }
 
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 79 - 13
Bin/Data/Scripts/15_Navigation.as

@@ -9,13 +9,8 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
 Vector3 endPos;
 Vector3 endPos;
 Array<Vector3> currentPath;
 Array<Vector3> currentPath;
-float yaw = 0.0f;
-float pitch = 0.0f;
-bool drawDebug = false;
 Node@ jackNode;
 Node@ jackNode;
 
 
 void Start()
 void Start()
@@ -25,10 +20,10 @@ void Start()
 
 
     // Create the scene content
     // Create the scene content
     CreateScene();
     CreateScene();
-    
+
     // Create the UI content
     // Create the UI content
     CreateUI();
     CreateUI();
-    
+
     // Setup the viewport for displaying the scene
     // Setup the viewport for displaying the scene
     SetupViewport();
     SetupViewport();
 
 
@@ -44,14 +39,14 @@ void CreateScene()
     // Also create a DebugRenderer component so that we can draw debug geometry
     // Also create a DebugRenderer component so that we can draw debug geometry
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("Octree");
     scene_.CreateComponent("DebugRenderer");
     scene_.CreateComponent("DebugRenderer");
-    
+
     // Create scene node & StaticModel component for showing a static plane
     // Create scene node & StaticModel component for showing a static plane
     Node@ planeNode = scene_.CreateChild("Plane");
     Node@ planeNode = scene_.CreateChild("Plane");
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     planeNode.scale = Vector3(100.0f, 1.0f, 100.0f);
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
     planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
-    
+
     // Create a Zone component for ambient lighting & fog control
     // Create a Zone component for ambient lighting & fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -113,12 +108,12 @@ void CreateScene()
     // physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example)
     // physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example)
     // it will use renderable geometry instead
     // it will use renderable geometry instead
     navMesh.Build();
     navMesh.Build();
-    
+
     // Create the camera. Limit far clip distance to match the fog
     // Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_.CreateChild("Camera");
     cameraNode = scene_.CreateChild("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     camera.farClip = 300.0f;
     camera.farClip = 300.0f;
-    
+
     // Set an initial position for the camera scene node above the plane
     // Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
 }
 }
@@ -133,7 +128,7 @@ void CreateUI()
     ui.cursor = cursor;
     ui.cursor = cursor;
     // Set starting position of the cursor at the rendering window center
     // Set starting position of the cursor at the rendering window center
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
-    
+
     // Construct new Text object, set string to display and font to use
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
     Text@ instructionText = ui.root.CreateChild("Text");
     instructionText.text =
     instructionText.text =
@@ -325,7 +320,7 @@ void FollowPath(float timeStep)
         float distance = (jackNode.position - nextWaypoint).length;
         float distance = (jackNode.position - nextWaypoint).length;
         if (move > distance)
         if (move > distance)
             move = distance;
             move = distance;
-        
+
         jackNode.LookAt(nextWaypoint, Vector3(0.0f, 1.0f, 0.0f));
         jackNode.LookAt(nextWaypoint, Vector3(0.0f, 1.0f, 0.0f));
         jackNode.Translate(Vector3(0.0f, 0.0f, 1.0f) * move);
         jackNode.Translate(Vector3(0.0f, 0.0f, 1.0f) * move);
 
 
@@ -375,3 +370,74 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
         }
         }
     }
     }
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element\">" +
+        "        <element type=\"Button\">" +
+        "            <attribute name=\"Name\" value=\"Button3\" />" +
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" +
+        "            <attribute name=\"Size\" value=\"96 96\" />" +
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" +
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" +
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" +
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" +
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" +
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"Label\" />" +
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" +
+        "                <attribute name=\"Text\" value=\"Teleport\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "                <attribute name=\"Text\" value=\"LSHIFT\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "                <attribute name=\"Text\" value=\"LEFT\" />" +
+        "            </element>" +
+        "        </element>" +
+        "        <element type=\"Button\">" +
+        "            <attribute name=\"Name\" value=\"Button4\" />" +
+        "            <attribute name=\"Position\" value=\"-120 -12\" />" +
+        "            <attribute name=\"Size\" value=\"96 96\" />" +
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" +
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" +
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" +
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" +
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" +
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"Label\" />" +
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" +
+        "                <attribute name=\"Text\" value=\"Obstacles\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "                <attribute name=\"Text\" value=\"MIDDLE\" />" +
+        "            </element>" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Set</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"MouseButtonBinding\" />" +
+        "            <attribute name=\"Text\" value=\"LEFT\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Debug</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 28 - 17
Bin/Data/Scripts/16_Chat.as

@@ -29,7 +29,7 @@ void Start()
 
 
     // Create the user interface
     // Create the user interface
     CreateUI();
     CreateUI();
-    
+
     // Subscribe to UI and network events
     // Subscribe to UI and network events
     SubscribeToEvents();
     SubscribeToEvents();
 }
 }
@@ -50,10 +50,10 @@ void CreateUI()
     buttonContainer.SetFixedSize(graphics.width, 20);
     buttonContainer.SetFixedSize(graphics.width, 20);
     buttonContainer.SetPosition(0, graphics.height - 20);
     buttonContainer.SetPosition(0, graphics.height - 20);
     buttonContainer.layoutMode = LM_HORIZONTAL;
     buttonContainer.layoutMode = LM_HORIZONTAL;
-    
+
     textEdit = buttonContainer.CreateChild("LineEdit");
     textEdit = buttonContainer.CreateChild("LineEdit");
     textEdit.SetStyleAuto();
     textEdit.SetStyleAuto();
-    
+
     sendButton = CreateButton("Send", 70);
     sendButton = CreateButton("Send", 70);
     connectButton = CreateButton("Connect", 90);
     connectButton = CreateButton("Connect", 90);
     disconnectButton = CreateButton("Disconnect", 100);
     disconnectButton = CreateButton("Disconnect", 100);
@@ -78,7 +78,7 @@ void SubscribeToEvents()
 
 
     // Subscribe to log messages so that we can pipe them to the chat window
     // Subscribe to log messages so that we can pipe them to the chat window
     SubscribeToEvent("LogMessage", "HandleLogMessage");
     SubscribeToEvent("LogMessage", "HandleLogMessage");
-    
+
     // Subscribe to network events
     // Subscribe to network events
     SubscribeToEvent("NetworkMessage", "HandleNetworkMessage");
     SubscribeToEvent("NetworkMessage", "HandleNetworkMessage");
     SubscribeToEvent("ServerConnected", "HandleConnectionStatus");
     SubscribeToEvent("ServerConnected", "HandleConnectionStatus");
@@ -89,7 +89,7 @@ void SubscribeToEvents()
 Button@ CreateButton(const String&in text, int width)
 Button@ CreateButton(const String&in text, int width)
 {
 {
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
-    
+
     Button@ button = buttonContainer.CreateChild("Button");
     Button@ button = buttonContainer.CreateChild("Button");
     button.SetStyleAuto();
     button.SetStyleAuto();
     button.SetFixedWidth(width);
     button.SetFixedWidth(width);
@@ -98,7 +98,7 @@ Button@ CreateButton(const String&in text, int width)
     buttonText.SetFont(font, 12);
     buttonText.SetFont(font, 12);
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.text = text;
     buttonText.text = text;
-    
+
     return button;
     return button;
 }
 }
 
 
@@ -111,7 +111,7 @@ void ShowChatText(const String& row)
     String allRows;
     String allRows;
     for (uint i = 0; i < chatHistory.length; ++i)
     for (uint i = 0; i < chatHistory.length; ++i)
         allRows += chatHistory[i] + "\n";
         allRows += chatHistory[i] + "\n";
-    
+
     chatHistoryText.text = allRows;
     chatHistoryText.text = allRows;
 }
 }
 
 
@@ -119,7 +119,7 @@ void UpdateButtons()
 {
 {
     Connection@ serverConnection = network.serverConnection;
     Connection@ serverConnection = network.serverConnection;
     bool serverRunning = network.serverRunning;
     bool serverRunning = network.serverRunning;
-    
+
     // Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     // Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     sendButton.visible = serverConnection !is null;
     sendButton.visible = serverConnection !is null;
     connectButton.visible = serverConnection is null && !serverRunning;
     connectButton.visible = serverConnection is null && !serverRunning;
@@ -139,7 +139,7 @@ void HandleSend(StringHash eventType, VariantMap& eventData)
         return; // Do not send an empty message
         return; // Do not send an empty message
 
 
     Connection@ serverConnection = network.serverConnection;
     Connection@ serverConnection = network.serverConnection;
-    
+
     if (serverConnection !is null)
     if (serverConnection !is null)
     {
     {
         // A VectorBuffer object is convenient for constructing a message to send
         // A VectorBuffer object is convenient for constructing a message to send
@@ -159,12 +159,12 @@ void HandleConnect(StringHash eventType, VariantMap& eventData)
         address = "localhost"; // Use localhost to connect if nothing else specified
         address = "localhost"; // Use localhost to connect if nothing else specified
     // Empty the text edit after reading the address to connect to
     // Empty the text edit after reading the address to connect to
     textEdit.text = "";
     textEdit.text = "";
-    
+
     // Connect to server, do not specify a client scene as we are not using scene replication, just messages.
     // Connect to server, do not specify a client scene as we are not using scene replication, just messages.
     // At connect time we could also send identity parameters (such as username) in a VariantMap, but in this
     // At connect time we could also send identity parameters (such as username) in a VariantMap, but in this
     // case we skip it for simplicity
     // case we skip it for simplicity
     network.Connect(address, CHAT_SERVER_PORT, null);
     network.Connect(address, CHAT_SERVER_PORT, null);
-    
+
     UpdateButtons();
     UpdateButtons();
 }
 }
 
 
@@ -177,7 +177,7 @@ void HandleDisconnect(StringHash eventType, VariantMap& eventData)
     // Or if we were running a server, stop it
     // Or if we were running a server, stop it
     else if (network.serverRunning)
     else if (network.serverRunning)
         network.StopServer();
         network.StopServer();
-    
+
     UpdateButtons();
     UpdateButtons();
 }
 }
 
 
@@ -195,21 +195,21 @@ void HandleNetworkMessage(StringHash eventType, VariantMap& eventData)
     {
     {
         VectorBuffer msg = eventData["Data"].GetBuffer();
         VectorBuffer msg = eventData["Data"].GetBuffer();
         String text = msg.ReadString();
         String text = msg.ReadString();
-        
+
         // If we are the server, prepend the sender's IP address and port and echo to everyone
         // If we are the server, prepend the sender's IP address and port and echo to everyone
         // If we are a client, just display the message
         // If we are a client, just display the message
         if (network.serverRunning)
         if (network.serverRunning)
         {
         {
             Connection@ sender = eventData["Connection"].GetPtr();
             Connection@ sender = eventData["Connection"].GetPtr();
-            
+
             text = sender.ToString() + " " + text;
             text = sender.ToString() + " " + text;
-            
+
             VectorBuffer sendMsg;
             VectorBuffer sendMsg;
             sendMsg.WriteString(text);
             sendMsg.WriteString(text);
             // Broadcast as in-order and reliable
             // Broadcast as in-order and reliable
             network.BroadcastMessage(MSG_CHAT, true, true, sendMsg);
             network.BroadcastMessage(MSG_CHAT, true, true, sendMsg);
         }
         }
-        
+
         ShowChatText(text);
         ShowChatText(text);
     }
     }
 }
 }
@@ -217,4 +217,15 @@ void HandleNetworkMessage(StringHash eventType, VariantMap& eventData)
 void HandleConnectionStatus(StringHash eventType, VariantMap& eventData)
 void HandleConnectionStatus(StringHash eventType, VariantMap& eventData)
 {
 {
     UpdateButtons();
     UpdateButtons();
-}
+}
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 44 - 40
Bin/Data/Scripts/17_SceneReplication.as

@@ -23,8 +23,6 @@ class Client
     Node@ object;
     Node@ object;
 }
 }
 
 
-Scene@ scene_;
-Node@ cameraNode;
 Text@ instructionsText;
 Text@ instructionsText;
 UIElement@ buttonContainer;
 UIElement@ buttonContainer;
 LineEdit@ textEdit;
 LineEdit@ textEdit;
@@ -32,8 +30,6 @@ Button@ connectButton;
 Button@ disconnectButton;
 Button@ disconnectButton;
 Button@ startServerButton;
 Button@ startServerButton;
 Array<Client> clients;
 Array<Client> clients;
-float yaw = 0.0f;
-float pitch = 1.0f;
 uint clientObjectID = 0;
 uint clientObjectID = 0;
 
 
 void Start()
 void Start()
@@ -43,10 +39,10 @@ void Start()
 
 
     // Create the scene content
     // Create the scene content
     CreateScene();
     CreateScene();
-    
+
     // Create the UI content
     // Create the UI content
     CreateUI();
     CreateUI();
-    
+
     // Setup the viewport for displaying the scene
     // Setup the viewport for displaying the scene
     SetupViewport();
     SetupViewport();
 
 
@@ -62,7 +58,7 @@ void CreateScene()
     // when a client connects
     // when a client connects
     scene_.CreateComponent("Octree", LOCAL);
     scene_.CreateComponent("Octree", LOCAL);
     scene_.CreateComponent("PhysicsWorld", LOCAL);
     scene_.CreateComponent("PhysicsWorld", LOCAL);
-    
+
     // All static scene content and the camera are also created as local, so that they are unaffected by scene replication and are
     // All static scene content and the camera are also created as local, so that they are unaffected by scene replication and are
     // not removed from the client upon connection. Create a Zone component first for ambient lighting & fog control.
     // not removed from the client upon connection. Create a Zone component first for ambient lighting & fog control.
     Node@ zoneNode = scene_.CreateChild("Zone", LOCAL);
     Node@ zoneNode = scene_.CreateChild("Zone", LOCAL);
@@ -71,7 +67,7 @@ void CreateScene()
     zone.ambientColor = Color(0.1f, 0.1f, 0.1f);
     zone.ambientColor = Color(0.1f, 0.1f, 0.1f);
     zone.fogStart = 100.0f;
     zone.fogStart = 100.0f;
     zone.fogEnd = 300.0f;
     zone.fogEnd = 300.0f;
-    
+
     // Create a directional light without shadows
     // Create a directional light without shadows
     Node@ lightNode = scene_.CreateChild("DirectionalLight", LOCAL);
     Node@ lightNode = scene_.CreateChild("DirectionalLight", LOCAL);
     lightNode.direction = Vector3(0.5f, -1.0f, 0.5f);
     lightNode.direction = Vector3(0.5f, -1.0f, 0.5f);
@@ -79,7 +75,7 @@ void CreateScene()
     light.lightType = LIGHT_DIRECTIONAL;
     light.lightType = LIGHT_DIRECTIONAL;
     light.color = Color(0.2f, 0.2f, 0.2f);
     light.color = Color(0.2f, 0.2f, 0.2f);
     light.specularIntensity = 1.0f;
     light.specularIntensity = 1.0f;
-    
+
     // Create a "floor" consisting of several tiles. Make the tiles physical but leave small cracks between them
     // Create a "floor" consisting of several tiles. Make the tiles physical but leave small cracks between them
     for (int y = -20; y <= 20; ++y)
     for (int y = -20; y <= 20; ++y)
     {
     {
@@ -98,12 +94,12 @@ void CreateScene()
             shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
             shape.SetBox(Vector3(1.0f, 1.0f, 1.0f));
         }
         }
     }
     }
-    
+
     // Create the camera. Limit far clip distance to match the fog
     // Create the camera. Limit far clip distance to match the fog
     cameraNode = scene_.CreateChild("Camera", LOCAL);
     cameraNode = scene_.CreateChild("Camera", LOCAL);
     Camera@ camera = cameraNode.CreateComponent("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     camera.farClip = 300.0f;
     camera.farClip = 300.0f;
-    
+
     // Set an initial position for the camera scene node above the plane
     // Set an initial position for the camera scene node above the plane
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
     cameraNode.position = Vector3(0.0f, 5.0f, 0.0f);
 }
 }
@@ -121,7 +117,7 @@ void CreateUI()
     ui.cursor = cursor;
     ui.cursor = cursor;
     // Set starting position of the cursor at the rendering window center
     // Set starting position of the cursor at the rendering window center
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
     cursor.SetPosition(graphics.width / 2, graphics.height / 2);
-    
+
     // Construct the instructions text element
     // Construct the instructions text element
     instructionsText = ui.root.CreateChild("Text");
     instructionsText = ui.root.CreateChild("Text");
     instructionsText.text = "Use WASD keys to move and RMB to rotate view";
     instructionsText.text = "Use WASD keys to move and RMB to rotate view";
@@ -137,14 +133,14 @@ void CreateUI()
     buttonContainer.SetFixedSize(500, 20);
     buttonContainer.SetFixedSize(500, 20);
     buttonContainer.SetPosition(20, 20);
     buttonContainer.SetPosition(20, 20);
     buttonContainer.layoutMode = LM_HORIZONTAL;
     buttonContainer.layoutMode = LM_HORIZONTAL;
-    
+
     textEdit = buttonContainer.CreateChild("LineEdit");
     textEdit = buttonContainer.CreateChild("LineEdit");
     textEdit.SetStyleAuto();
     textEdit.SetStyleAuto();
-    
+
     connectButton = CreateButton("Connect", 90);
     connectButton = CreateButton("Connect", 90);
     disconnectButton = CreateButton("Disconnect", 100);
     disconnectButton = CreateButton("Disconnect", 100);
     startServerButton = CreateButton("Start Server", 110);
     startServerButton = CreateButton("Start Server", 110);
-    
+
     UpdateButtons();
     UpdateButtons();
 }
 }
 
 
@@ -159,12 +155,12 @@ void SubscribeToEvents()
 {
 {
     // Subscribe to fixed timestep physics updates for setting or applying controls
     // Subscribe to fixed timestep physics updates for setting or applying controls
     SubscribeToEvent("PhysicsPreStep", "HandlePhysicsPreStep");
     SubscribeToEvent("PhysicsPreStep", "HandlePhysicsPreStep");
-    
+
     // Subscribe HandlePostUpdate() method for processing update events. Subscribe to PostUpdate instead
     // Subscribe HandlePostUpdate() method for processing update events. Subscribe to PostUpdate instead
     // of the usual Update so that physics simulation has already proceeded for the frame, and can
     // of the usual Update so that physics simulation has already proceeded for the frame, and can
     // accurately follow the object with the camera
     // accurately follow the object with the camera
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
-    
+
     // Subscribe to button actions
     // Subscribe to button actions
     SubscribeToEvent(connectButton, "Released", "HandleConnect");
     SubscribeToEvent(connectButton, "Released", "HandleConnect");
     SubscribeToEvent(disconnectButton, "Released", "HandleDisconnect");
     SubscribeToEvent(disconnectButton, "Released", "HandleDisconnect");
@@ -183,16 +179,16 @@ void SubscribeToEvents()
 Button@ CreateButton(const String& text, int width)
 Button@ CreateButton(const String& text, int width)
 {
 {
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
     Font@ font = cache.GetResource("Font", "Fonts/Anonymous Pro.ttf");
-    
+
     Button@ button = buttonContainer.CreateChild("Button");
     Button@ button = buttonContainer.CreateChild("Button");
     button.SetStyleAuto();
     button.SetStyleAuto();
     button.SetFixedWidth(width);
     button.SetFixedWidth(width);
-    
+
     Text@ buttonText = button.CreateChild("Text");
     Text@ buttonText = button.CreateChild("Text");
     buttonText.SetFont(font, 12);
     buttonText.SetFont(font, 12);
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.SetAlignment(HA_CENTER, VA_CENTER);
     buttonText.text = text;
     buttonText.text = text;
-    
+
     return button;
     return button;
 }
 }
 
 
@@ -200,7 +196,7 @@ void UpdateButtons()
 {
 {
     Connection@ serverConnection = network.serverConnection;
     Connection@ serverConnection = network.serverConnection;
     bool serverRunning = network.serverRunning;
     bool serverRunning = network.serverRunning;
-    
+
     // Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     // Show and hide buttons so that eg. Connect and Disconnect are never shown at the same time
     connectButton.visible = serverConnection is null && !serverRunning;
     connectButton.visible = serverConnection is null && !serverRunning;
     disconnectButton.visible = serverConnection !is null || serverRunning;
     disconnectButton.visible = serverConnection !is null || serverRunning;
@@ -217,7 +213,7 @@ Node@ CreateControllableObject()
     StaticModel@ ballObject = ballNode.CreateComponent("StaticModel");
     StaticModel@ ballObject = ballNode.CreateComponent("StaticModel");
     ballObject.model = cache.GetResource("Model", "Models/Sphere.mdl");
     ballObject.model = cache.GetResource("Model", "Models/Sphere.mdl");
     ballObject.material = cache.GetResource("Material", "Materials/StoneSmall.xml");
     ballObject.material = cache.GetResource("Material", "Materials/StoneSmall.xml");
-    
+
     // Create the physics components
     // Create the physics components
     RigidBody@ body = ballNode.CreateComponent("RigidBody");
     RigidBody@ body = ballNode.CreateComponent("RigidBody");
     body.mass = 1.0f;
     body.mass = 1.0f;
@@ -227,12 +223,12 @@ Node@ CreateControllableObject()
     body.angularDamping = 0.5f;
     body.angularDamping = 0.5f;
     CollisionShape@ shape = ballNode.CreateComponent("CollisionShape");
     CollisionShape@ shape = ballNode.CreateComponent("CollisionShape");
     shape.SetSphere(1.0f);
     shape.SetSphere(1.0f);
-    
+
     // Create a random colored point light at the ball so that can see better where is going
     // Create a random colored point light at the ball so that can see better where is going
     Light@ light = ballNode.CreateComponent("Light");
     Light@ light = ballNode.CreateComponent("Light");
     light.range = 3.0f;
     light.range = 3.0f;
     light.color = Color(0.5f + (RandomInt() & 1) * 0.5f, 0.5f + (RandomInt() & 1) * 0.5f, 0.5f + (RandomInt() & 1) * 0.5f);
     light.color = Color(0.5f + (RandomInt() & 1) * 0.5f, 0.5f + (RandomInt() & 1) * 0.5f, 0.5f + (RandomInt() & 1) * 0.5f);
-    
+
     return ballNode;
     return ballNode;
 }
 }
 
 
@@ -240,10 +236,10 @@ void MoveCamera()
 {
 {
     // Right mouse button controls mouse cursor visibility: hide when pressed
     // Right mouse button controls mouse cursor visibility: hide when pressed
     ui.cursor.visible = !input.mouseButtonDown[MOUSEB_RIGHT];
     ui.cursor.visible = !input.mouseButtonDown[MOUSEB_RIGHT];
-    
+
     // Mouse sensitivity as degrees per pixel
     // Mouse sensitivity as degrees per pixel
     const float MOUSE_SENSITIVITY = 0.1f;
     const float MOUSE_SENSITIVITY = 0.1f;
-    
+
     // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch and only move the camera
     // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch and only move the camera
     // when the cursor is hidden
     // when the cursor is hidden
     if (!ui.cursor.visible)
     if (!ui.cursor.visible)
@@ -253,7 +249,7 @@ void MoveCamera()
         pitch += MOUSE_SENSITIVITY * mouseMove.y;
         pitch += MOUSE_SENSITIVITY * mouseMove.y;
         pitch = Clamp(pitch, 1.0f, 90.0f);
         pitch = Clamp(pitch, 1.0f, 90.0f);
     }
     }
-    
+
     // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
     // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
     cameraNode.rotation = Quaternion(pitch, yaw, 0.0f);
     cameraNode.rotation = Quaternion(pitch, yaw, 0.0f);
 
 
@@ -265,13 +261,13 @@ void MoveCamera()
         if (ballNode !is null)
         if (ballNode !is null)
         {
         {
             const float CAMERA_DISTANCE = 5.0f;
             const float CAMERA_DISTANCE = 5.0f;
-            
+
             // Move camera some distance away from the ball
             // Move camera some distance away from the ball
             cameraNode.position = ballNode.position + cameraNode.rotation * Vector3(0.0f, 0.0f, -1.0f) * CAMERA_DISTANCE;
             cameraNode.position = ballNode.position + cameraNode.rotation * Vector3(0.0f, 0.0f, -1.0f) * CAMERA_DISTANCE;
             showInstructions = true;
             showInstructions = true;
         }
         }
     }
     }
-    
+
     instructionsText.visible = showInstructions;
     instructionsText.visible = showInstructions;
 }
 }
 
 
@@ -287,15 +283,15 @@ void HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
     // and sets them to its server connection object, so that they will be sent to the server automatically at a
     // and sets them to its server connection object, so that they will be sent to the server automatically at a
     // fixed rate, by default 30 FPS. The server will actually apply the controls (authoritative simulation.)
     // fixed rate, by default 30 FPS. The server will actually apply the controls (authoritative simulation.)
     Connection@ serverConnection = network.serverConnection;
     Connection@ serverConnection = network.serverConnection;
-    
+
     // Client: collect controls
     // Client: collect controls
     if (serverConnection !is null)
     if (serverConnection !is null)
     {
     {
         Controls controls;
         Controls controls;
-        
+
         // Copy mouse yaw
         // Copy mouse yaw
         controls.yaw = yaw;
         controls.yaw = yaw;
-        
+
         // Only apply WASD controls if there is no focused UI element
         // Only apply WASD controls if there is no focused UI element
         if (ui.focusElement is null)
         if (ui.focusElement is null)
         {
         {
@@ -304,7 +300,7 @@ void HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
             controls.Set(CTRL_LEFT, input.keyDown['A']);
             controls.Set(CTRL_LEFT, input.keyDown['A']);
             controls.Set(CTRL_RIGHT, input.keyDown['D']);
             controls.Set(CTRL_RIGHT, input.keyDown['D']);
         }
         }
-        
+
         serverConnection.controls = controls;
         serverConnection.controls = controls;
         // In case the server wants to do position-based interest management using the NetworkPriority components, we should also
         // In case the server wants to do position-based interest management using the NetworkPriority components, we should also
         // tell it our observer (camera) position. In this sample it is not in use, but eg. the NinjaSnowWar game uses it
         // tell it our observer (camera) position. In this sample it is not in use, but eg. the NinjaSnowWar game uses it
@@ -323,9 +319,9 @@ void HandlePhysicsPreStep(StringHash eventType, VariantMap& eventData)
 
 
             // Torque is relative to the forward vector
             // Torque is relative to the forward vector
             Quaternion rotation(0.0f, connection.controls.yaw, 0.0f);
             Quaternion rotation(0.0f, connection.controls.yaw, 0.0f);
-            
+
             const float MOVE_TORQUE = 3.0f;
             const float MOVE_TORQUE = 3.0f;
-            
+
             // Movement torque is applied before each simulation step, which happen at 60 FPS. This makes the simulation
             // Movement torque is applied before each simulation step, which happen at 60 FPS. This makes the simulation
             // independent from rendering framerate. We could also apply forces (which would enable in-air control),
             // independent from rendering framerate. We could also apply forces (which would enable in-air control),
             // but want to emphasize that it's a ball which should only control its motion by rolling along the ground
             // but want to emphasize that it's a ball which should only control its motion by rolling along the ground
@@ -346,11 +342,11 @@ void HandleConnect(StringHash eventType, VariantMap& eventData)
     String address = textEdit.text.Trimmed();
     String address = textEdit.text.Trimmed();
     if (address.empty)
     if (address.empty)
         address = "localhost"; // Use localhost to connect if nothing else specified
         address = "localhost"; // Use localhost to connect if nothing else specified
-    
+
     // Connect to server, specify scene to use as a client for replication
     // Connect to server, specify scene to use as a client for replication
     clientObjectID = 0; // Reset own object ID from possible previous connection
     clientObjectID = 0; // Reset own object ID from possible previous connection
     network.Connect(address, SERVER_PORT, scene_);
     network.Connect(address, SERVER_PORT, scene_);
-    
+
     UpdateButtons();
     UpdateButtons();
 }
 }
 
 
@@ -371,14 +367,14 @@ void HandleDisconnect(StringHash eventType, VariantMap& eventData)
         network.StopServer();
         network.StopServer();
         scene_.Clear(true, false);
         scene_.Clear(true, false);
     }
     }
-    
+
     UpdateButtons();
     UpdateButtons();
 }
 }
 
 
 void HandleStartServer(StringHash eventType, VariantMap& eventData)
 void HandleStartServer(StringHash eventType, VariantMap& eventData)
 {
 {
     network.StartServer(SERVER_PORT);
     network.StartServer(SERVER_PORT);
-    
+
     UpdateButtons();
     UpdateButtons();
 }
 }
 
 
@@ -392,7 +388,7 @@ void HandleClientConnected(StringHash eventType, VariantMap& eventData)
     // When a client connects, assign to scene to begin scene replication
     // When a client connects, assign to scene to begin scene replication
     Connection@ newConnection = eventData["Connection"].GetPtr();
     Connection@ newConnection = eventData["Connection"].GetPtr();
     newConnection.scene = scene_;
     newConnection.scene = scene_;
-    
+
     // Then create a controllable object for that client
     // Then create a controllable object for that client
     Node@ newObject = CreateControllableObject();
     Node@ newObject = CreateControllableObject();
     Client newClient;
     Client newClient;
@@ -425,3 +421,11 @@ void HandleClientObjectID(StringHash eventType, VariantMap& eventData)
 {
 {
     clientObjectID = eventData["ID"].GetUInt();
     clientObjectID = eventData["ID"].GetUInt();
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 108 - 49
Bin/Data/Scripts/18_CharacterDemo.as

@@ -22,8 +22,8 @@ const float BRAKE_FORCE = 0.2f;
 const float JUMP_FORCE = 7.0f;
 const float JUMP_FORCE = 7.0f;
 const float YAW_SENSITIVITY = 0.1f;
 const float YAW_SENSITIVITY = 0.1f;
 const float INAIR_THRESHOLD_TIME = 0.1f;
 const float INAIR_THRESHOLD_TIME = 0.1f;
+bool firstPerson = false; // First person camera flag
 
 
-Scene@ scene_;
 Node@ characterNode;
 Node@ characterNode;
 
 
 void Start()
 void Start()
@@ -40,13 +40,6 @@ void Start()
     // Create the UI content
     // Create the UI content
     CreateInstructions();
     CreateInstructions();
 
 
-    // Activate mobile stuff when appropriate
-    if (GetPlatform() == "Android" || GetPlatform() == "iOS")
-    {
-        SetLogoVisible(false);
-        InitTouchInput();
-    }
-
     // Subscribe to necessary events
     // Subscribe to necessary events
     SubscribeToEvents();
     SubscribeToEvents();
 }
 }
@@ -203,9 +196,8 @@ void SubscribeToEvents()
     // Subscribe to PostUpdate event for updating the camera position after physics simulation
     // Subscribe to PostUpdate event for updating the camera position after physics simulation
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
 
 
-    // Subscribe to mobile touch events
-    if (touchEnabled)
-        SubscribeToTouchEvents();
+    // Unsubscribe the SceneUpdate event from base class as the camera node is being controlled in HandlePostUpdate() in this sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 }
 
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -220,48 +212,70 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Clear previous controls
     // Clear previous controls
     character.controls.Set(CTRL_FORWARD | CTRL_BACK | CTRL_LEFT | CTRL_RIGHT | CTRL_JUMP, false);
     character.controls.Set(CTRL_FORWARD | CTRL_BACK | CTRL_LEFT | CTRL_RIGHT | CTRL_JUMP, false);
 
 
+    // Update controls using touch utility
     if (touchEnabled)
     if (touchEnabled)
-    {
-        // Update controls using touch (mobile)
         UpdateTouches(character.controls);
         UpdateTouches(character.controls);
-    }
-    else
+
+    // Update controls using keys (desktop)
+    if (ui.focusElement is null)
     {
     {
-        // Update controls using keys (desktop)
-        if (ui.focusElement is null)
+        if (touchEnabled || !useGyroscope)
         {
         {
             character.controls.Set(CTRL_FORWARD, input.keyDown['W']);
             character.controls.Set(CTRL_FORWARD, input.keyDown['W']);
             character.controls.Set(CTRL_BACK, input.keyDown['S']);
             character.controls.Set(CTRL_BACK, input.keyDown['S']);
             character.controls.Set(CTRL_LEFT, input.keyDown['A']);
             character.controls.Set(CTRL_LEFT, input.keyDown['A']);
             character.controls.Set(CTRL_RIGHT, input.keyDown['D']);
             character.controls.Set(CTRL_RIGHT, input.keyDown['D']);
-            character.controls.Set(CTRL_JUMP, input.keyDown[KEY_SPACE]);
+        }
+        character.controls.Set(CTRL_JUMP, input.keyDown[KEY_SPACE]);
 
 
-            // Add character yaw & pitch from the mouse motion
+        // Add character yaw & pitch from the mouse motion or touch input
+        if (touchEnabled)
+        {
+            for (uint i = 0; i < input.numTouches; ++i)
+            {
+                TouchState@ state = input.touches[i];
+                if (state.touchedElement is null) // Touch on empty space
+                {
+                    Camera@ camera = cameraNode.GetComponent("Camera");
+                    if (camera is null)
+                        return;
+
+                    character.controls.yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x;
+                    character.controls.pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y;
+                }
+            }
+        }
+        else
+        {
             character.controls.yaw += input.mouseMoveX * YAW_SENSITIVITY;
             character.controls.yaw += input.mouseMoveX * YAW_SENSITIVITY;
             character.controls.pitch += input.mouseMoveY * YAW_SENSITIVITY;
             character.controls.pitch += input.mouseMoveY * YAW_SENSITIVITY;
-            // Limit pitch
-            character.controls.pitch = Clamp(character.controls.pitch, -80.0f, 80.0f);
+        }
+        // Limit pitch
+        character.controls.pitch = Clamp(character.controls.pitch, -80.0f, 80.0f);
 
 
-            // Switch between 1st and 3rd person
-            if (input.keyPress['F'])
-                firstPerson = newFirstPerson = !firstPerson;
+        // Switch between 1st and 3rd person
+        if (input.keyPress['F'])
+            firstPerson = !firstPerson;
 
 
-            // Check for loading / saving the scene
-            if (input.keyPress[KEY_F5])
-            {
-                File saveFile(fileSystem.programDir + "Data/Scenes/CharacterDemo.xml", FILE_WRITE);
-                scene_.SaveXML(saveFile);
-            }
-            if (input.keyPress[KEY_F7])
-            {
-                File loadFile(fileSystem.programDir + "Data/Scenes/CharacterDemo.xml", FILE_READ);
-                scene_.LoadXML(loadFile);
-                // After loading we have to reacquire the character scene node, as it has been recreated
-                // Simply find by name as there's only one of them
-                characterNode = scene_.GetChild("Jack", true);
-                if (characterNode is null)
-                    return;
-            }
+        // Turn on/off gyroscope on mobile platform
+        if (input.keyPress['G'])
+            useGyroscope = !useGyroscope;
+
+        // Check for loading / saving the scene
+        if (input.keyPress[KEY_F5])
+        {
+            File saveFile(fileSystem.programDir + "Data/Scenes/CharacterDemo.xml", FILE_WRITE);
+            scene_.SaveXML(saveFile);
+        }
+        if (input.keyPress[KEY_F7])
+        {
+            File loadFile(fileSystem.programDir + "Data/Scenes/CharacterDemo.xml", FILE_READ);
+            scene_.LoadXML(loadFile);
+            // After loading we have to reacquire the character scene node, as it has been recreated
+            // Simply find by name as there's only one of them
+            characterNode = scene_.GetChild("Jack", true);
+            if (characterNode is null)
+                return;
         }
         }
     }
     }
 
 
@@ -370,13 +384,13 @@ class Character : ScriptObject
             }
             }
         }
         }
     }
     }
-    
+
     void FixedUpdate(float timeStep)
     void FixedUpdate(float timeStep)
     {
     {
         /// \todo Could cache the components for faster access instead of finding them each frame
         /// \todo Could cache the components for faster access instead of finding them each frame
         RigidBody@ body = node.GetComponent("RigidBody");
         RigidBody@ body = node.GetComponent("RigidBody");
         AnimationController@ animCtrl = node.GetComponent("AnimationController");
         AnimationController@ animCtrl = node.GetComponent("AnimationController");
-        
+
         // Update the in air timer. Reset if grounded
         // Update the in air timer. Reset if grounded
         if (!onGround)
         if (!onGround)
             inAirTimer += timeStep;
             inAirTimer += timeStep;
@@ -384,14 +398,14 @@ class Character : ScriptObject
             inAirTimer = 0.0f;
             inAirTimer = 0.0f;
         // When character has been in air less than 1/10 second, it's still interpreted as being on ground
         // When character has been in air less than 1/10 second, it's still interpreted as being on ground
         bool softGrounded = inAirTimer < INAIR_THRESHOLD_TIME;
         bool softGrounded = inAirTimer < INAIR_THRESHOLD_TIME;
-        
+
         // Update movement & animation
         // Update movement & animation
         Quaternion rot = node.rotation;
         Quaternion rot = node.rotation;
         Vector3 moveDir(0.0f, 0.0f, 0.0f);
         Vector3 moveDir(0.0f, 0.0f, 0.0f);
         Vector3 velocity = body.linearVelocity;
         Vector3 velocity = body.linearVelocity;
         // Velocity on the XZ plane
         // Velocity on the XZ plane
         Vector3 planeVelocity(velocity.x, 0.0f, velocity.z);
         Vector3 planeVelocity(velocity.x, 0.0f, velocity.z);
-        
+
         if (controls.IsDown(CTRL_FORWARD))
         if (controls.IsDown(CTRL_FORWARD))
             moveDir += Vector3(0.0f, 0.0f, 1.0f);
             moveDir += Vector3(0.0f, 0.0f, 1.0f);
         if (controls.IsDown(CTRL_BACK))
         if (controls.IsDown(CTRL_BACK))
@@ -400,20 +414,20 @@ class Character : ScriptObject
             moveDir += Vector3(-1.0f, 0.0f, 0.0f);
             moveDir += Vector3(-1.0f, 0.0f, 0.0f);
         if (controls.IsDown(CTRL_RIGHT))
         if (controls.IsDown(CTRL_RIGHT))
             moveDir += Vector3(1.0f, 0.0f, 0.0f);
             moveDir += Vector3(1.0f, 0.0f, 0.0f);
-        
+
         // Normalize move vector so that diagonal strafing is not faster
         // Normalize move vector so that diagonal strafing is not faster
         if (moveDir.lengthSquared > 0.0f)
         if (moveDir.lengthSquared > 0.0f)
             moveDir.Normalize();
             moveDir.Normalize();
-        
+
         // If in air, allow control, but slower than when on ground
         // If in air, allow control, but slower than when on ground
         body.ApplyImpulse(rot * moveDir * (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
         body.ApplyImpulse(rot * moveDir * (softGrounded ? MOVE_FORCE : INAIR_MOVE_FORCE));
-        
+
         if (softGrounded)
         if (softGrounded)
         {
         {
             // When on ground, apply a braking force to limit maximum ground velocity
             // When on ground, apply a braking force to limit maximum ground velocity
             Vector3 brakeForce = -planeVelocity * BRAKE_FORCE;
             Vector3 brakeForce = -planeVelocity * BRAKE_FORCE;
             body.ApplyImpulse(brakeForce);
             body.ApplyImpulse(brakeForce);
-            
+
             // Jump. Must release jump control inbetween jumps
             // Jump. Must release jump control inbetween jumps
             if (controls.IsDown(CTRL_JUMP))
             if (controls.IsDown(CTRL_JUMP))
             {
             {
@@ -426,7 +440,7 @@ class Character : ScriptObject
             else
             else
                 okToJump = true;
                 okToJump = true;
         }
         }
-        
+
         // Play walk animation if moving on ground, otherwise fade it out
         // Play walk animation if moving on ground, otherwise fade it out
         if (softGrounded && !moveDir.Equals(Vector3(0.0f, 0.0f, 0.0f)))
         if (softGrounded && !moveDir.Equals(Vector3(0.0f, 0.0f, 0.0f)))
             animCtrl.PlayExclusive("Models/Jack_Walk.ani", 0, true, 0.2f);
             animCtrl.PlayExclusive("Models/Jack_Walk.ani", 0, true, 0.2f);
@@ -439,3 +453,48 @@ class Character : ScriptObject
         onGround = false;
         onGround = false;
     }
     }
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element\">" +
+        "        <element type=\"Button\">" +
+        "            <attribute name=\"Name\" value=\"Button3\" />" +
+        "            <attribute name=\"Position\" value=\"-120 -120\" />" +
+        "            <attribute name=\"Size\" value=\"96 96\" />" +
+        "            <attribute name=\"Horiz Alignment\" value=\"Right\" />" +
+        "            <attribute name=\"Vert Alignment\" value=\"Bottom\" />" +
+        "            <attribute name=\"Texture\" value=\"Texture2D;Textures/TouchInput.png\" />" +
+        "            <attribute name=\"Image Rect\" value=\"96 0 192 96\" />" +
+        "            <attribute name=\"Hover Image Offset\" value=\"0 0\" />" +
+        "            <attribute name=\"Pressed Image Offset\" value=\"0 0\" />" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"Label\" />" +
+        "                <attribute name=\"Horiz Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Vert Alignment\" value=\"Center\" />" +
+        "                <attribute name=\"Color\" value=\"0 0 0 1\" />" +
+        "                <attribute name=\"Text\" value=\"Gyroscope\" />" +
+        "            </element>" +
+        "            <element type=\"Text\">" +
+        "                <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "                <attribute name=\"Text\" value=\"G\" />" +
+        "            </element>" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">1st/3rd</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"F\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Jump</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 31 - 9
Bin/Data/Scripts/19_VehicleDemo.as

@@ -17,15 +17,13 @@ const float ENGINE_POWER = 10.0f;
 const float DOWN_FORCE = 10.0f;
 const float DOWN_FORCE = 10.0f;
 const float MAX_WHEEL_ANGLE = 22.5f;
 const float MAX_WHEEL_ANGLE = 22.5f;
 
 
-Scene@ scene_;
-Node@ cameraNode;
 Node@ vehicleNode;
 Node@ vehicleNode;
 
 
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
     SampleStart();
     SampleStart();
-    
+
     // Create static scene content
     // Create static scene content
     CreateScene();
     CreateScene();
 
 
@@ -52,7 +50,7 @@ void CreateScene()
     Camera@ camera = cameraNode.CreateComponent("Camera");
     Camera@ camera = cameraNode.CreateComponent("Camera");
     camera.farClip = 500.0f;
     camera.farClip = 500.0f;
     renderer.viewports[0] = Viewport(scene_, camera);
     renderer.viewports[0] = Viewport(scene_, camera);
-    
+
     // Create static scene content. First create a zone for ambient lighting and fog control
     // Create static scene content. First create a zone for ambient lighting and fog control
     Node@ zoneNode = scene_.CreateChild("Zone");
     Node@ zoneNode = scene_.CreateChild("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
     Zone@ zone = zoneNode.CreateComponent("Zone");
@@ -84,7 +82,7 @@ void CreateScene()
     // The terrain consists of large triangles, which fits well for occlusion rendering, as a hill can occlude all
     // The terrain consists of large triangles, which fits well for occlusion rendering, as a hill can occlude all
     // terrain patches and other objects behind it
     // terrain patches and other objects behind it
     terrain.occluder = true;
     terrain.occluder = true;
-    
+
     RigidBody@ body = terrainNode.CreateComponent("RigidBody");
     RigidBody@ body = terrainNode.CreateComponent("RigidBody");
     body.collisionLayer = 2; // Use layer bitmask 2 for static geometry
     body.collisionLayer = 2; // Use layer bitmask 2 for static geometry
     CollisionShape@ shape = terrainNode.CreateComponent("CollisionShape");
     CollisionShape@ shape = terrainNode.CreateComponent("CollisionShape");
@@ -105,7 +103,7 @@ void CreateScene()
         object.model = cache.GetResource("Model", "Models/Mushroom.mdl");
         object.model = cache.GetResource("Model", "Models/Mushroom.mdl");
         object.material = cache.GetResource("Material", "Materials/Mushroom.xml");
         object.material = cache.GetResource("Material", "Materials/Mushroom.xml");
         object.castShadows = true;
         object.castShadows = true;
-        
+
         RigidBody@ body = objectNode.CreateComponent("RigidBody");
         RigidBody@ body = objectNode.CreateComponent("RigidBody");
         body.collisionLayer = 2;
         body.collisionLayer = 2;
         CollisionShape@ shape = objectNode.CreateComponent("CollisionShape");
         CollisionShape@ shape = objectNode.CreateComponent("CollisionShape");
@@ -128,7 +126,7 @@ void CreateInstructions()
 {
 {
     // Construct new Text object, set string to display and font to use
     // Construct new Text object, set string to display and font to use
     Text@ instructionText = ui.root.CreateChild("Text");
     Text@ instructionText = ui.root.CreateChild("Text");
-    instructionText.text = "Use WASD keys to drive, mouse to rotate camera\n"
+    instructionText.text = "Use WASD keys to drive, mouse/touch to rotate camera\n"
         "F5 to save scene, F7 to load";
         "F5 to save scene, F7 to load";
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
     instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
     // The text has multiple rows. Center them in relation to each other
     // The text has multiple rows. Center them in relation to each other
@@ -147,6 +145,9 @@ void SubscribeToEvents()
 
 
     // Subscribe to PostUpdate event for updating the camera position after physics simulation
     // Subscribe to PostUpdate event for updating the camera position after physics simulation
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
     SubscribeToEvent("PostUpdate", "HandlePostUpdate");
+
+    // Unsubscribe the SceneUpdate event from base class as the camera node is being controlled in HandlePostUpdate() in this sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 }
 
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -167,8 +168,27 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
         vehicle.controls.Set(CTRL_RIGHT, input.keyDown['D']);
         vehicle.controls.Set(CTRL_RIGHT, input.keyDown['D']);
 
 
         // Add yaw & pitch from the mouse motion. Used only for the camera, does not affect motion
         // Add yaw & pitch from the mouse motion. Used only for the camera, does not affect motion
-        vehicle.controls.yaw += input.mouseMoveX * YAW_SENSITIVITY;
-        vehicle.controls.pitch += input.mouseMoveY * YAW_SENSITIVITY;
+        if (touchEnabled)
+        {
+            for (uint i = 0; i < input.numTouches; ++i)
+            {
+                TouchState@ state = input.touches[i];
+                if (state.touchedElement is null) // Touch on empty space
+                {
+                    Camera@ camera = cameraNode.GetComponent("Camera");
+                    if (camera is null)
+                        return;
+
+                    vehicle.controls.yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x;
+                    vehicle.controls.pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y;
+                }
+            }
+        }
+        else
+        {
+            vehicle.controls.yaw += input.mouseMoveX * YAW_SENSITIVITY;
+            vehicle.controls.pitch += input.mouseMoveY * YAW_SENSITIVITY;
+        }
         // Limit pitch
         // Limit pitch
         vehicle.controls.pitch = Clamp(vehicle.controls.pitch, 0.0f, 80.0f);
         vehicle.controls.pitch = Clamp(vehicle.controls.pitch, 0.0f, 80.0f);
 
 
@@ -369,3 +389,5 @@ class Vehicle : ScriptObject
     }
     }
 }
 }
 
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 23 - 6
Bin/Data/Scripts/20_HugeObjectCount.as

@@ -7,11 +7,7 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
 Array<Node@> boxNodes;
 Array<Node@> boxNodes;
-float yaw = 0.0f;
-float pitch = 0.0f;
 bool animate = false;
 bool animate = false;
 bool useGroups = false;
 bool useGroups = false;
 
 
@@ -207,11 +203,32 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
         useGroups = !useGroups;
         useGroups = !useGroups;
         CreateScene();
         CreateScene();
     }
     }
-    
+
     // Move the camera, scale movement with time step
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
     MoveCamera(timeStep);
-    
+
     // Animate scene if enabled
     // Animate scene if enabled
     if (animate)
     if (animate)
         AnimateObjects(timeStep);
         AnimateObjects(timeStep);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Group</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"G\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Animation</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"SPACE\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 3 - 4
Bin/Data/Scripts/23_Water.as

@@ -5,14 +5,10 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
 Node@ reflectionCameraNode;
 Node@ reflectionCameraNode;
 Node@ waterNode;
 Node@ waterNode;
 Plane waterPlane;
 Plane waterPlane;
 Plane waterClipPlane;
 Plane waterClipPlane;
-float yaw = 0.0f;
-float pitch = 0.0f;
 
 
 void Start()
 void Start()
 {
 {
@@ -225,3 +221,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move the camera, scale movement with time step
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
     MoveCamera(timeStep);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 28 - 7
Bin/Data/Scripts/24_Urho2DSprite.as

@@ -6,8 +6,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
 Array<Node@> spriteNodes;
 Array<Node@> spriteNodes;
 
 
 void Start()
 void Start()
@@ -65,7 +63,7 @@ void CreateScene()
     {
     {
         Node@ spriteNode = scene_.CreateChild("StaticSprite2D");
         Node@ spriteNode = scene_.CreateChild("StaticSprite2D");
         spriteNode.position = Vector3(Random(-halfWidth, halfWidth), Random(-halfHeight, halfHeight), 0.0f);
         spriteNode.position = Vector3(Random(-halfWidth, halfWidth), Random(-halfHeight, halfHeight), 0.0f);
-        
+
         StaticSprite2D@ staticSprite = spriteNode.CreateComponent("StaticSprite2D");
         StaticSprite2D@ staticSprite = spriteNode.CreateComponent("StaticSprite2D");
         // Set color
         // Set color
         staticSprite.color = Color(Random(1.0f), Random(1.0f), Random(1.0f), 1.0f);
         staticSprite.color = Color(Random(1.0f), Random(1.0f), Random(1.0f), 1.0f);
@@ -73,7 +71,7 @@ void CreateScene()
         staticSprite.blendMode = BLEND_ALPHA;
         staticSprite.blendMode = BLEND_ALPHA;
         // Set sprite
         // Set sprite
         staticSprite.sprite = sprite;
         staticSprite.sprite = sprite;
-        
+
         spriteNode.vars["MoveSpeed"] = Vector3(Random(-2.0f, 2.0f), Random(-2.0f, 2.0f), 0.0f);
         spriteNode.vars["MoveSpeed"] = Vector3(Random(-2.0f, 2.0f), Random(-2.0f, 2.0f), 0.0f);
         spriteNode.vars["RotateSpeed"] = Random(-90.0f, 90.0f);
         spriteNode.vars["RotateSpeed"] = Random(-90.0f, 90.0f);
 
 
@@ -123,7 +121,7 @@ void MoveCamera(float timeStep)
 
 
     // Movement speed as world units per second
     // Movement speed as world units per second
     const float MOVE_SPEED = 4.0f;
     const float MOVE_SPEED = 4.0f;
-    
+
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     if (input.keyDown['W'])
     if (input.keyDown['W'])
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
@@ -139,7 +137,7 @@ void MoveCamera(float timeStep)
         Camera@ camera = cameraNode.GetComponent("Camera");
         Camera@ camera = cameraNode.GetComponent("Camera");
         camera.zoom = camera.zoom * 1.01f;
         camera.zoom = camera.zoom * 1.01f;
     }
     }
-    
+
     if (input.keyDown[KEY_PAGEDOWN])
     if (input.keyDown[KEY_PAGEDOWN])
     {
     {
         Camera@ camera = cameraNode.GetComponent("Camera");
         Camera@ camera = cameraNode.GetComponent("Camera");
@@ -151,6 +149,9 @@ void SubscribeToEvents()
 {
 {
     // Subscribe HandleUpdate() function for processing update events
     // Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate");
     SubscribeToEvent("Update", "HandleUpdate");
+
+    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 }
 
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -171,7 +172,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
 
 
         Vector3 moveSpeed = spriteNode.vars["MoveSpeed"].GetVector3();
         Vector3 moveSpeed = spriteNode.vars["MoveSpeed"].GetVector3();
         Vector3 newPosition = spriteNode.position + moveSpeed * timeStep;
         Vector3 newPosition = spriteNode.position + moveSpeed * timeStep;
-        
+
         if (newPosition.x < -halfWidth || newPosition.x > halfWidth)
         if (newPosition.x < -halfWidth || newPosition.x > halfWidth)
         {
         {
             newPosition.x = spriteNode.position.x;
             newPosition.x = spriteNode.position.x;
@@ -191,3 +192,23 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     }
     }
 }
 }
 
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 13 - 2
Bin/Data/Scripts/25_Urho2DParticle.as

@@ -6,8 +6,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
 Node@ particleNode;
 Node@ particleNode;
 
 
 void Start()
 void Start()
@@ -92,7 +90,13 @@ void SetupViewport()
 
 
 void SubscribeToEvents()
 void SubscribeToEvents()
 {
 {
+    // Subscribe HandleMouseMove() function for tracking mouse/touch move events
     SubscribeToEvent("MouseMove", "HandleMouseMove");
     SubscribeToEvent("MouseMove", "HandleMouseMove");
+    if (touchEnabled)
+        SubscribeToEvent("TouchMove", "HandleMouseMove");
+
+    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 }
 
 
 void HandleMouseMove(StringHash eventType, VariantMap& eventData)
 void HandleMouseMove(StringHash eventType, VariantMap& eventData)
@@ -106,3 +110,10 @@ void HandleMouseMove(StringHash eventType, VariantMap& eventData)
     }
     }
 }
 }
 
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 11 - 0
Bin/Data/Scripts/26_ConsoleInput.as

@@ -238,3 +238,14 @@ void HandleInput(const String&in input)
             Print("Please answer 'y' or 'n'.");
             Print("Please answer 'y' or 'n'.");
     }
     }
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button2']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
+        "        <attribute name=\"Is Visible\" value=\"false\" />" +
+        "    </add>" +
+        "</patch>";

+ 28 - 8
Bin/Data/Scripts/27_Urho2DPhysics.as

@@ -5,9 +5,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -49,7 +46,7 @@ void CreateScene()
 
 
     // Create 2D physics world component
     // Create 2D physics world component
     scene_.CreateComponent("PhysicsWorld2D");
     scene_.CreateComponent("PhysicsWorld2D");
-    
+
     Sprite2D@ boxSprite = cache.GetResource("Sprite2D", "Urho2D/Box.png");
     Sprite2D@ boxSprite = cache.GetResource("Sprite2D", "Urho2D/Box.png");
     Sprite2D@ ballSprite = cache.GetResource("Sprite2D", "Urho2D/Ball.png");
     Sprite2D@ ballSprite = cache.GetResource("Sprite2D", "Urho2D/Ball.png");
 
 
@@ -57,10 +54,10 @@ void CreateScene()
     Node@ groundNode = scene_.CreateChild("Ground");
     Node@ groundNode = scene_.CreateChild("Ground");
     groundNode.position = Vector3(0.0f, -3.0f, 0.0f);
     groundNode.position = Vector3(0.0f, -3.0f, 0.0f);
     groundNode.scale = Vector3(200.0f, 1.0f, 0.0f);
     groundNode.scale = Vector3(200.0f, 1.0f, 0.0f);
-    
+
     // Create 2D rigid body for gound
     // Create 2D rigid body for gound
     RigidBody2D@ groundBody = groundNode.CreateComponent("RigidBody2D");
     RigidBody2D@ groundBody = groundNode.CreateComponent("RigidBody2D");
-    
+
     StaticSprite2D@ groundSprite = groundNode.CreateComponent("StaticSprite2D");
     StaticSprite2D@ groundSprite = groundNode.CreateComponent("StaticSprite2D");
     groundSprite.sprite = boxSprite;
     groundSprite.sprite = boxSprite;
 
 
@@ -146,7 +143,7 @@ void MoveCamera(float timeStep)
 
 
     // Movement speed as world units per second
     // Movement speed as world units per second
     const float MOVE_SPEED = 4.0f;
     const float MOVE_SPEED = 4.0f;
-    
+
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     if (input.keyDown['W'])
     if (input.keyDown['W'])
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
@@ -162,7 +159,7 @@ void MoveCamera(float timeStep)
         Camera@ camera = cameraNode.GetComponent("Camera");
         Camera@ camera = cameraNode.GetComponent("Camera");
         camera.zoom = camera.zoom * 1.01f;
         camera.zoom = camera.zoom * 1.01f;
     }
     }
-    
+
     if (input.keyDown[KEY_PAGEDOWN])
     if (input.keyDown[KEY_PAGEDOWN])
     {
     {
         Camera@ camera = cameraNode.GetComponent("Camera");
         Camera@ camera = cameraNode.GetComponent("Camera");
@@ -174,6 +171,9 @@ void SubscribeToEvents()
 {
 {
     // Subscribe HandleUpdate() function for processing update events
     // Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate");
     SubscribeToEvent("Update", "HandleUpdate");
+
+    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 }
 
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -185,3 +185,23 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     MoveCamera(timeStep);
     MoveCamera(timeStep);
 }
 }
 
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 26 - 6
Bin/Data/Scripts/28_Urho2DPhysicsRope.as

@@ -6,9 +6,6 @@
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -51,7 +48,7 @@ void CreateScene()
     // Create 2D physics world component
     // Create 2D physics world component
     PhysicsWorld2D@ physicsWorld = scene_.CreateComponent("PhysicsWorld2D");
     PhysicsWorld2D@ physicsWorld = scene_.CreateComponent("PhysicsWorld2D");
     physicsWorld.drawJoint = true;
     physicsWorld.drawJoint = true;
-    
+
     // Create ground.
     // Create ground.
     Node@ groundNode = scene_.CreateChild("Ground");
     Node@ groundNode = scene_.CreateChild("Ground");
     // Create 2D rigid body for gound
     // Create 2D rigid body for gound
@@ -138,7 +135,7 @@ void MoveCamera(float timeStep)
 
 
     // Movement speed as world units per second
     // Movement speed as world units per second
     const float MOVE_SPEED = 4.0f;
     const float MOVE_SPEED = 4.0f;
-    
+
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
     if (input.keyDown['W'])
     if (input.keyDown['W'])
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
         cameraNode.Translate(Vector3(0.0f, 1.0f, 0.0f) * MOVE_SPEED * timeStep);
@@ -154,7 +151,7 @@ void MoveCamera(float timeStep)
         Camera@ camera = cameraNode.GetComponent("Camera");
         Camera@ camera = cameraNode.GetComponent("Camera");
         camera.zoom = camera.zoom * 1.01f;
         camera.zoom = camera.zoom * 1.01f;
     }
     }
-    
+
     if (input.keyDown[KEY_PAGEDOWN])
     if (input.keyDown[KEY_PAGEDOWN])
     {
     {
         Camera@ camera = cameraNode.GetComponent("Camera");
         Camera@ camera = cameraNode.GetComponent("Camera");
@@ -166,6 +163,9 @@ void SubscribeToEvents()
 {
 {
     // Subscribe HandleUpdate() function for processing update events
     // Subscribe HandleUpdate() function for processing update events
     SubscribeToEvent("Update", "HandleUpdate");
     SubscribeToEvent("Update", "HandleUpdate");
+
+    // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
+    UnsubscribeFromEvent("SceneUpdate");
 }
 }
 
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -180,3 +180,23 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     physicsWorld.DrawDebugGeometry();
     physicsWorld.DrawDebugGeometry();
 }
 }
 
 
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions =
+        "<patch>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom In</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button0']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEUP\" />" +
+        "        </element>" +
+        "    </add>" +
+        "    <remove sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/attribute[@name='Is Visible']\" />" +
+        "    <replace sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]/element[./attribute[@name='Name' and @value='Label']]/attribute[@name='Text']/@value\">Zoom Out</replace>" +
+        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Button1']]\">" +
+        "        <element type=\"Text\">" +
+        "            <attribute name=\"Name\" value=\"KeyBinding\" />" +
+        "            <attribute name=\"Text\" value=\"PAGEDOWN\" />" +
+        "        </element>" +
+        "    </add>" +
+        "</patch>";

+ 6 - 8
Bin/Data/Scripts/30_LightAnimation.as

@@ -1,14 +1,9 @@
-/// Light animation example.
-/// This sample is base on StaticScene, and it demonstrates:
-///     - Usage of attribute animation for light color animation
+// Light animation example.
+// This sample is base on StaticScene, and it demonstrates:
+//     - Usage of attribute animation for light color animation
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -169,3 +164,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move the camera, scale movement with time step
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
     MoveCamera(timeStep);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 6 - 8
Bin/Data/Scripts/31_MaterialAnimation.as

@@ -1,14 +1,9 @@
-/// Material animation example.
-/// This sample is base on StaticScene, and it demonstrates:
-///     - Usage of material shader animation for mush room material
+// Material animation example.
+// This sample is base on StaticScene, and it demonstrates:
+//     - Usage of material shader animation for mush room material
 
 
 #include "Scripts/Utilities/Sample.as"
 #include "Scripts/Utilities/Sample.as"
 
 
-Scene@ scene_;
-Node@ cameraNode;
-float yaw = 0.0f;
-float pitch = 0.0f;
-
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
@@ -158,3 +153,6 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
     // Move the camera, scale movement with time step
     // Move the camera, scale movement with time step
     MoveCamera(timeStep);
     MoveCamera(timeStep);
 }
 }
+
+// Create XML patch instructions for screen joystick layout specific to this sample app
+String patchInstructions = "";

+ 100 - 7
Bin/Data/Scripts/Utilities/Sample.as

@@ -5,11 +5,29 @@
 //    - Toggle rendering options from the keys 1-8;
 //    - Toggle rendering options from the keys 1-8;
 //    - Take screenshots with key 9;
 //    - Take screenshots with key 9;
 //    - Handle Esc key down to hide Console or exit application;
 //    - Handle Esc key down to hide Console or exit application;
+//    - Init touch input on mobile platform using screen joysticks (patched for each individual sample)
 
 
 Sprite@ logoSprite;
 Sprite@ logoSprite;
+Scene@ scene_;
+uint screenJoystickIndex = M_MAX_UNSIGNED; // Screen joystick index for navigational controls (mobile platforms only)
+uint screenJoystickSettingsIndex = M_MAX_UNSIGNED; // Screen joystick index for settings (mobile platforms only)
+bool touchEnabled = false; // Flag to indicate whether touch input has been enabled
+bool paused = false; // Pause flag
+bool drawDebug = false; // Draw debug geometry flag
+Node@ cameraNode; // Camera scene node
+float yaw = 0.0f; // Camera yaw angle
+float pitch = 0.0f; // Camera pitch angle
+const float TOUCH_SENSITIVITY = 2;
 
 
 void SampleStart()
 void SampleStart()
 {
 {
+    if (GetPlatform() == "Android" || GetPlatform() == "iOS" || input.touchEmulation)
+        // On mobile platform, enable touch by adding a screen joystick
+        InitTouchInput();
+    else if (input.numJoysticks == 0)
+        // On desktop platform, do not detect touch when we already got a joystick
+        SubscribeToEvent("TouchBegin", "HandleTouchBegin");
+
     // Create logo
     // Create logo
     CreateLogo();
     CreateLogo();
 
 
@@ -21,6 +39,25 @@ void SampleStart()
 
 
     // Subscribe key down event
     // Subscribe key down event
     SubscribeToEvent("KeyDown", "HandleKeyDown");
     SubscribeToEvent("KeyDown", "HandleKeyDown");
+
+    // Subscribe scene update event
+    SubscribeToEvent("SceneUpdate", "HandleSceneUpdate");
+}
+
+void InitTouchInput()
+{
+    touchEnabled = true;
+
+    XMLFile@ layout = cache.GetResource("XMLFile", "UI/ScreenJoystick_Samples.xml");
+    if (!patchInstructions.empty)
+    {
+        // Patch the screen joystick layout further on demand
+        XMLFile@ patchFile = XMLFile();
+        if (patchFile.FromString(patchInstructions))
+            layout.Patch(patchFile);
+    }
+    screenJoystickIndex = input.AddScreenJoystick(layout, cache.GetResource("XMLFile", "UI/DefaultStyle.xml"));
+    input.screenJoystickVisible[0] = true;
 }
 }
 
 
 void SetLogoVisible(bool enable)
 void SetLogoVisible(bool enable)
@@ -56,10 +93,10 @@ void CreateLogo()
 
 
     // Set logo sprite alignment
     // Set logo sprite alignment
     logoSprite.SetAlignment(HA_LEFT, VA_BOTTOM);
     logoSprite.SetAlignment(HA_LEFT, VA_BOTTOM);
-    
+
     // Make logo not fully opaque to show the scene underneath
     // Make logo not fully opaque to show the scene underneath
     logoSprite.opacity = 0.75f;
     logoSprite.opacity = 0.75f;
-    
+
     // Set a low priority for the logo so that other UI elements can be drawn on top
     // Set a low priority for the logo so that other UI elements can be drawn on top
     logoSprite.priority = -100;
     logoSprite.priority = -100;
 }
 }
@@ -81,6 +118,7 @@ void CreateConsoleAndDebugHud()
     // Create console
     // Create console
     Console@ console = engine.CreateConsole();
     Console@ console = engine.CreateConsole();
     console.defaultStyle = xmlFile;
     console.defaultStyle = xmlFile;
+    console.background.opacity = 0.8f;
 
 
     // Create debug HUD
     // Create debug HUD
     DebugHud@ debugHud = engine.CreateDebugHud();
     DebugHud@ debugHud = engine.CreateDebugHud();
@@ -111,8 +149,22 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
     // Common rendering quality controls, only when UI has no focused element
     // Common rendering quality controls, only when UI has no focused element
     if (ui.focusElement is null)
     if (ui.focusElement is null)
     {
     {
+        // Preferences / Pause
+        if (key == KEY_SELECT && touchEnabled)
+        {
+            paused = !paused;
+
+            if (screenJoystickSettingsIndex == M_MAX_UNSIGNED)
+            {
+                // Lazy initialization
+                screenJoystickSettingsIndex = input.AddScreenJoystick(cache.GetResource("XMLFile", "UI/ScreenJoystickSettings_Samples.xml"), cache.GetResource("XMLFile", "UI/DefaultStyle.xml"));
+            }
+            else
+                input.screenJoystickVisible[screenJoystickSettingsIndex] = paused;
+        }
+
         // Texture quality
         // Texture quality
-        if (key == '1')
+        else if (key == '1')
         {
         {
             int quality = renderer.textureQuality;
             int quality = renderer.textureQuality;
             ++quality;
             ++quality;
@@ -130,7 +182,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 quality = QUALITY_LOW;
                 quality = QUALITY_LOW;
             renderer.materialQuality = quality;
             renderer.materialQuality = quality;
         }
         }
-        
+
         // Specular lighting
         // Specular lighting
         else if (key == '3')
         else if (key == '3')
             renderer.specularLighting = !renderer.specularLighting;
             renderer.specularLighting = !renderer.specularLighting;
@@ -148,7 +200,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 shadowMapSize = 512;
                 shadowMapSize = 512;
             renderer.shadowMapSize = shadowMapSize;
             renderer.shadowMapSize = shadowMapSize;
         }
         }
-        
+
         // Shadow depth and filtering quality
         // Shadow depth and filtering quality
         else if (key == '6')
         else if (key == '6')
         {
         {
@@ -158,7 +210,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 quality = SHADOWQUALITY_LOW_16BIT;
                 quality = SHADOWQUALITY_LOW_16BIT;
             renderer.shadowQuality = quality;
             renderer.shadowQuality = quality;
         }
         }
-        
+
         // Occlusion culling
         // Occlusion culling
         else if (key == '7')
         else if (key == '7')
         {
         {
@@ -166,7 +218,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             occlusion = !occlusion;
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
         }
-        
+
         // Instancing
         // Instancing
         else if (key == '8')
         else if (key == '8')
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
@@ -182,3 +234,44 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
         }
         }
     }
     }
 }
 }
+
+void HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
+{
+    // Move the camera by touch, if the camera node is initialized by descendant sample class
+    if (touchEnabled && cameraNode !is null)
+    {
+        for (uint i = 0; i < input.numTouches; ++i)
+        {
+            TouchState@ state = input.touches[i];
+            if (state.touchedElement is null) // Touch on empty space
+            {
+                if (state.delta.x !=0 || state.delta.y !=0)
+                {
+                    Camera@ camera = cameraNode.GetComponent("Camera");
+                    if (camera is null)
+                        return;
+
+                    yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.x;
+                    pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * state.delta.y;
+
+                    // Construct new orientation for the camera scene node from yaw and pitch; roll is fixed to zero
+                    cameraNode.rotation = Quaternion(pitch, yaw, 0.0f);
+                }
+                else
+                {
+                    // Move the cursor to the touch position
+                    Cursor@ cursor = ui.cursor;
+                    if (cursor !is null && cursor.visible)
+                        cursor.position = state.position;
+                }
+            }
+        }
+    }
+}
+
+void HandleTouchBegin(StringHash eventType, VariantMap& eventData)
+{
+    // On some platforms like Windows the presence of touch input can only be detected dynamically
+    InitTouchInput();
+    UnsubscribeFromEvent("TouchBegin");
+}

+ 21 - 163
Bin/Data/Scripts/Utilities/Touch.as

@@ -1,144 +1,47 @@
 // Mobile framework for Android/iOS
 // Mobile framework for Android/iOS
 // Gamepad from NinjaSnowWar
 // Gamepad from NinjaSnowWar
-// Gyroscope (activated by default)
 // Touches patterns:
 // Touches patterns:
 //     - 1 finger touch  = pick object through raycast
 //     - 1 finger touch  = pick object through raycast
 //     - 1 or 2 fingers drag  = rotate camera
 //     - 1 or 2 fingers drag  = rotate camera
-//     - 3 fingers touch = switch between first/third person view
-//     - 4 fingers touch = switch shadows on/off
 //     - 2 fingers sliding in opposite direction (up/down) = zoom in/out
 //     - 2 fingers sliding in opposite direction (up/down) = zoom in/out
 
 
-// 3 fingers touch & 4 fingers touch could be used to switch gyroscope on/off, activate/deactivate secondary viewport, activate a panel GUI, switch debug HUD/geometry, toggle console, switch the gyroscope...
+// Setup: Call the update function 'UpdateTouches()' from HandleUpdate or equivalent update handler function
 
 
-// Setup:
-// - On init, call this script using '#include "Scripts/Utilities/Touch.as"' then 'InitTouchInput()' on mobile platforms
-//   -> to detect platform, use 'if (GetPlatform() == "Android" || GetPlatform() == "iOS")'
-// - Subscribe to touch events (Begin, Move, End) using 'SubscribeToTouchEvents()'
-// - Call the update function 'UpdateTouches()' from HandleUpdate or equivalent update handler function
-
-const float TOUCH_SENSITIVITY = 5.0;
 const float GYROSCOPE_THRESHOLD = 0.1;
 const float GYROSCOPE_THRESHOLD = 0.1;
 const float CAMERA_MIN_DIST = 1.0f;
 const float CAMERA_MIN_DIST = 1.0f;
-const float CAMERA_INITIAL_DIST = 5.0f;
 const float CAMERA_MAX_DIST = 20.0f;
 const float CAMERA_MAX_DIST = 20.0f;
 
 
-bool firstPerson = false;
-bool touchEnabled = false;
 bool zoom = false;
 bool zoom = false;
-bool newFirstPerson = false;
-bool shadowMode = true;
-
-Node@ cameraNode;
-float cameraDistance = CAMERA_INITIAL_DIST;
-
-BorderImage@ moveButton;
-BorderImage@ fireButton;
-int touchButtonSize = 96;
-int touchButtonBorder = 12;
-int moveTouchID = -1;
-int rotateTouchID = -1;
-int fireTouchID = -1;
-
-void InitTouchInput() // Create Gamepad Buttons
-{
-    moveButton = ui.root.CreateChild("BorderImage");
-    moveButton.texture = cache.GetResource("Texture2D", "Textures/TouchInput.png");
-    moveButton.imageRect = IntRect(0, 0, 96, 96);
-    moveButton.SetAlignment(HA_LEFT, VA_BOTTOM);
-    moveButton.SetPosition(touchButtonBorder, -touchButtonBorder);
-    moveButton.SetSize(touchButtonSize, touchButtonSize);
-    moveButton.opacity = 0.25;
-
-    fireButton = ui.root.CreateChild("BorderImage");
-    fireButton.texture = cache.GetResource("Texture2D", "Textures/TouchInput.png");
-    fireButton.imageRect = IntRect(96, 0, 192, 96);
-    fireButton.SetAlignment(HA_RIGHT, VA_BOTTOM);
-    fireButton.SetPosition(-touchButtonBorder, -touchButtonBorder);
-    fireButton.SetSize(touchButtonSize, touchButtonSize);
-    fireButton.opacity = 0.25;
-    
-    touchEnabled = true;
-}
-
-void SubscribeToTouchEvents()
-{
-    SubscribeToEvent("TouchBegin", "HandleTouchBegin");
-    SubscribeToEvent("TouchEnd", "HandleTouchEnd");
-}
+bool useGyroscope = false;
+float cameraDistance = 5.0;
 
 
 void UpdateTouches(Controls& controls) // Called from HandleUpdate
 void UpdateTouches(Controls& controls) // Called from HandleUpdate
 {
 {
-    Camera@ camera = cameraNode.GetComponent("Camera");
     zoom = false; // reset bool
     zoom = false; // reset bool
 
 
-    // Touch Inputs
-    if (touchEnabled)
+    // Zoom in/out
+    if (input.numTouches == 2)
     {
     {
-        // Zoom in/out
-        if (input.numTouches == 2)
-        {
-            TouchState@ touch1 = input.touches[0];
-            TouchState@ touch2 = input.touches[1];
-
-            // Check for zoom pattern (touches moving in opposite directions)
-            if ((touch1.delta.y > 0 && touch2.delta.y < 0) || (touch1.delta.y < 0 && touch2.delta.y > 0))
-                zoom = true;
-            else 
-                zoom = false;
-
-            if (zoom)
-            {
-                int sens = 0;
-                // Check for zoom direction (in/out)
-                if (Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y))
-                    sens = -1;
-                else
-                    sens = 1;
-                cameraDistance += Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50;
-                cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST); // Restrict zoom range to [1;20]
-            }
-        }
-
-        // Switch 1st/3rd person mode
-        if (input.numTouches == 3)
-            newFirstPerson = !firstPerson;
+        TouchState@ touch1 = input.touches[0];
+        TouchState@ touch2 = input.touches[1];
 
 
-        // Switch draw debug
-        if (input.numTouches == 4)
-            shadowMode = !renderer.drawShadows;
+        // Check for zoom pattern (touches moving in opposite directions)
+        if (touch1.touchedElement is null && touch2.touchedElement is null && ((touch1.delta.y > 0 && touch2.delta.y < 0) || (touch1.delta.y < 0 && touch2.delta.y > 0)))
+            zoom = true;
+        else
+            zoom = false;
 
 
-        // Rotate and Move
-        if (!zoom)
+        if (zoom)
         {
         {
-            for (uint i = 0; i < input.numTouches; ++i)
-            {
-                TouchState@ touch = input.touches[i];
-
-                if (touch.touchID == rotateTouchID)
-                {
-                    controls.yaw += TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.x;
-                    controls.pitch += TOUCH_SENSITIVITY * camera.fov / graphics.height * touch.delta.y;
-                    controls.pitch = Clamp(controls.pitch, -80.0f, 80.0f); // Limit pitch
-                }
-
-                if (touch.touchID == moveTouchID)
-                {
-                    int relX = touch.position.x - moveButton.screenPosition.x - touchButtonSize / 2;
-                    int relY = touch.position.y - moveButton.screenPosition.y - touchButtonSize / 2;
-                    if (relY < 0 && Abs(relX * 3 / 2) < Abs(relY))
-                        controls.Set(CTRL_FORWARD, true);
-                    if (relY > 0 && Abs(relX * 3 / 2) < Abs(relY))
-                        controls.Set(CTRL_BACK, true);
-                    if (relX < 0 && Abs(relY * 3 / 2) < Abs(relX))
-                        controls.Set(CTRL_LEFT, true);
-                    if (relX > 0 && Abs(relY * 3 / 2) < Abs(relX))
-                        controls.Set(CTRL_RIGHT, true);
-                }
-            }
+            int sens = 0;
+            // Check for zoom direction (in/out)
+            if (Abs(touch1.position.y - touch2.position.y) > Abs(touch1.lastPosition.y - touch2.lastPosition.y))
+                sens = -1;
+            else
+                sens = 1;
+            cameraDistance += Abs(touch1.delta.y - touch2.delta.y) * sens * TOUCH_SENSITIVITY / 50;
+            cameraDistance = Clamp(cameraDistance, CAMERA_MIN_DIST, CAMERA_MAX_DIST); // Restrict zoom range to [1;20]
         }
         }
-
-        if (fireTouchID >= 0)
-            controls.Set(CTRL_JUMP, true);
     }
     }
 
 
     // Gyroscope (emulated by SDL through a virtual joystick)
     // Gyroscope (emulated by SDL through a virtual joystick)
@@ -158,48 +61,3 @@ void UpdateTouches(Controls& controls) // Called from HandleUpdate
         }
         }
     }
     }
 }
 }
-
-void HandleTouchBegin(StringHash eventType, VariantMap& eventData)
-{
-    int touchID = eventData["TouchID"].GetInt(); // Get #touches or dragging value
-    IntVector2 pos(eventData["X"].GetInt(), eventData["Y"].GetInt()); // Get touch coordinates
-    UIElement@ element = ui.GetElementAt(pos, false); // Get gamepad UIElement touched (if any)
-
-    // Check for gamepad button touched. If none, rotate
-    if (element is moveButton)
-        moveTouchID = touchID;
-    else if (element is fireButton)
-        fireTouchID = touchID;
-    else
-        rotateTouchID = touchID;
-
-    // Init Raycast (for example to acquire a target)
-    Camera@ camera = cameraNode.GetComponent("Camera");
-    Ray cameraRay = camera.GetScreenRay(float(eventData["X"].GetInt()) / graphics.width, float(eventData["Y"].GetInt()) / graphics.height);
-
-    // Raycast of RigidBodies
-    PhysicsRaycastResult result = scene_.physicsWorld.RaycastSingle(cameraRay, camera.farClip, 2); // NB: here we restrict targets to layer 2
-    if (result.body !is null)
-        log.Info("Physics raycast hit " + result.body.node.name);
-
-    // Raycast of drawable components (for targets without physics)
-    RayQueryResult result2 = scene_.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, camera.farClip, DRAWABLE_GEOMETRY);
-    if (result2.drawable !is null)
-        log.Info("Drawable raycast hit " + result2.drawable.node.name);
-}
-
-void HandleTouchEnd(StringHash eventType, VariantMap& eventData)
-{
-    int touchID = eventData["TouchID"].GetInt();
-
-    if (touchID == moveTouchID)
-        moveTouchID = -1;
-    if (touchID == rotateTouchID)
-        rotateTouchID = -1;
-    if (touchID == fireTouchID)
-        fireTouchID = -1;
-
-    // On-release Update
-    firstPerson = newFirstPerson;
-    renderer.drawShadows = shadowMode;
-}