SimpleApplication.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. using System;
  2. using System.Threading;
  3. using System.Threading.Tasks;
  4. using Urho.Actions;
  5. using Urho.Gui;
  6. using System.IO;
  7. using System.Linq;
  8. using Urho.Shapes;
  9. namespace Urho
  10. {
  11. public class SimpleApplication : Application
  12. {
  13. public SimpleApplication(ApplicationOptions options) : base(options) {}
  14. public static Task<SimpleApplication> RunAsync(int width = 600, int height = 500)
  15. {
  16. #if DESKTOP
  17. return RunAsync(new ApplicationOptions(assetsFolder: "Data") { Width = width, Height = height, ResizableWindow = true });
  18. #endif
  19. return RunAsync(new ApplicationOptions(assetsFolder: null));
  20. }
  21. public static Task<SimpleApplication> RunAsync(ApplicationOptions options)
  22. {
  23. #if DESKTOP
  24. var dataDir = options.ResourcePaths?.FirstOrDefault();
  25. Environment.CurrentDirectory = Path.GetDirectoryName(typeof(SimpleApplication).Assembly.Location);
  26. if (!File.Exists("CoreData.pak")) {
  27. using (Stream input = typeof(SimpleApplication).Assembly.GetManifestResourceStream("Urho.CoreData.pak"))
  28. using (Stream output = File.Create(Path.Combine("CoreData.pak")))
  29. input.CopyTo(output);
  30. }
  31. if (!string.IsNullOrEmpty(dataDir))
  32. Directory.CreateDirectory("Data");
  33. #endif
  34. #if !IOS && !UWP
  35. var taskSource = new TaskCompletionSource<SimpleApplication>();
  36. Action callback = null;
  37. callback = () => {
  38. Started -= callback;
  39. taskSource.TrySetResult(Current as SimpleApplication);
  40. };
  41. Started += callback;
  42. Task.Factory.StartNew(() => new SimpleApplication(options).Run(),
  43. CancellationToken.None,
  44. TaskCreationOptions.DenyChildAttach,
  45. SynchronizationContext.Current == null ? TaskScheduler.Default : TaskScheduler.FromCurrentSynchronizationContext());
  46. return taskSource.Task;
  47. #else
  48. var app = new SimpleApplication(options);
  49. app.Run(); //for iOS and UWP it's not blocking
  50. return Task.FromResult(app);
  51. #endif
  52. }
  53. public Node CameraNode { get; private set; }
  54. public Camera Camera { get; private set; }
  55. public Scene Scene { get; private set; }
  56. public Octree Octree { get; private set; }
  57. public Node RootNode { get; private set; }
  58. public Node LightNode { get; private set; }
  59. public Light Light { get; private set; }
  60. public Viewport Viewport { get; private set; }
  61. public bool MoveCamera { get; set; }
  62. public float Yaw { get; set; }
  63. public float Pitch { get; set; }
  64. protected override void Start()
  65. {
  66. // 3D scene with Octree
  67. Scene = new Scene(Context);
  68. Octree = Scene.CreateComponent<Octree>();
  69. RootNode = Scene.CreateChild("RootNode");
  70. RootNode.Position = new Vector3(x: 0, y: 0, z: 8);
  71. LightNode = Scene.CreateChild("DirectionalLight");
  72. LightNode.SetDirection(new Vector3(0.5f, 0.0f, 0.8f));
  73. Light = LightNode.CreateComponent<Light>();
  74. Light.LightType = LightType.Directional;
  75. Light.CastShadows = true;
  76. Light.ShadowBias = new BiasParameters(0.00025f, 0.5f);
  77. Light.ShadowCascade = new CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
  78. Light.SpecularIntensity = 0.5f;
  79. Light.Color = new Color(1.2f, 1.2f, 1.2f);
  80. // Camera
  81. CameraNode = Scene.CreateChild(name: "Camera");
  82. Camera = CameraNode.CreateComponent<Camera>();
  83. // Viewport
  84. Viewport = new Viewport(Context, Scene, Camera, null);
  85. Renderer.SetViewport(0, Viewport);
  86. Viewport.SetClearColor(Color.White);
  87. // Subscribe to Esc key:
  88. Input.SubscribeToKeyDown(args => { if (args.Key == Key.Esc) Exit(); });
  89. }
  90. public float MoveSpeed { get; set; } = 10f;
  91. protected override void OnUpdate(float timeStep)
  92. {
  93. if (MoveCamera)
  94. {
  95. if (!Options.TouchEmulation && Platform != Platforms.Android && Platform != Platforms.iOS)
  96. MoveCameraMouse(timeStep);
  97. else
  98. MoveCameraTouches(timeStep);
  99. if (Input.GetKeyDown(Key.W)) CameraNode.Translate(Vector3.UnitZ * MoveSpeed * timeStep);
  100. if (Input.GetKeyDown(Key.S)) CameraNode.Translate(-Vector3.UnitZ * MoveSpeed * timeStep);
  101. if (Input.GetKeyDown(Key.A)) CameraNode.Translate(-Vector3.UnitX * MoveSpeed * timeStep);
  102. if (Input.GetKeyDown(Key.D)) CameraNode.Translate(Vector3.UnitX * MoveSpeed * timeStep);
  103. }
  104. base.OnUpdate(timeStep);
  105. }
  106. protected void MoveCameraMouse(float timeStep)
  107. {
  108. const float mouseSensitivity = .05f;
  109. if (UI.FocusElement != null)
  110. return;
  111. var mouseMove = Input.MouseMove;
  112. Yaw += mouseSensitivity * mouseMove.X;
  113. Pitch += mouseSensitivity * mouseMove.Y;
  114. Pitch = MathHelper.Clamp(Pitch, -90, 90);
  115. CameraNode.Rotation = new Quaternion(Pitch, Yaw, 0);
  116. }
  117. protected void MoveCameraTouches(float timeStep)
  118. {
  119. var input = Input;
  120. for (uint i = 0, num = input.NumTouches; i < num; ++i)
  121. {
  122. TouchState state = input.GetTouch(i);
  123. if (state.TouchedElement != null)
  124. continue;
  125. if (state.Delta.X != 0 || state.Delta.Y != 0)
  126. {
  127. var camera = CameraNode.GetComponent<Camera>();
  128. if (camera == null)
  129. return;
  130. var graphics = Graphics;
  131. Yaw += 2 * camera.Fov / graphics.Height * state.Delta.X;
  132. Pitch += 2 * camera.Fov / graphics.Height * state.Delta.Y;
  133. CameraNode.Rotation = new Quaternion(Pitch, Yaw, 0);
  134. }
  135. else
  136. {
  137. var cursor = UI.Cursor;
  138. if (cursor != null && cursor.Visible)
  139. cursor.Position = state.Position;
  140. }
  141. }
  142. }
  143. }
  144. public class Bar : Component
  145. {
  146. Node barNode;
  147. Node textNode;
  148. Color color;
  149. string name;
  150. public float Value
  151. {
  152. get { return barNode.Scale.Y; }
  153. set { barNode.RunActionsAsync(new EaseBackOut(new ScaleTo(3f, 1, value, 1))); }
  154. }
  155. public Bar(string name, Color color)
  156. {
  157. this.name = name;
  158. this.color = color;
  159. ReceiveSceneUpdates = true;
  160. }
  161. public override void OnAttachedToNode(Node node)
  162. {
  163. barNode = node.CreateChild();
  164. barNode.Scale = new Vector3(1, 0, 1);
  165. var box = barNode.CreateComponent<Box>();
  166. box.Color = color;
  167. textNode = node.CreateChild();
  168. textNode.Position = new Vector3(0, 3, 0);
  169. var text3D = textNode.CreateComponent<Text3D>();
  170. text3D.SetFont(CoreAssets.Fonts.AnonymousPro, 60);
  171. text3D.TextEffect = TextEffect.Stroke;
  172. text3D.Text = name;
  173. base.OnAttachedToNode(node);
  174. }
  175. protected override void OnUpdate(float timeStep)
  176. {
  177. var pos = barNode.Position;
  178. var scale = barNode.Scale;
  179. barNode.Position = new Vector3(pos.X, scale.Y / 2f, pos.Z);
  180. textNode.Position = new Vector3(-0.5f, scale.Y + 0.5f, 0);
  181. }
  182. }
  183. }