19_VehicleDemo.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. //
  2. // Copyright (c) 2008-2015 the Urho3D project.
  3. // Copyright (c) 2015 Xamarin Inc
  4. // Copyright (c) 2016 THUNDERBEAST GAMES LLC
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. using AtomicEngine;
  25. namespace FeatureExamples
  26. {
  27. public class VehicleDemo : Sample
  28. {
  29. Scene scene;
  30. Vehicle vehicle;
  31. const float CameraDistance = 10.0f;
  32. public VehicleDemo() : base() { }
  33. public override void Start()
  34. {
  35. base.Start();
  36. // Create static scene content
  37. CreateScene();
  38. // Create the controllable vehicle
  39. CreateVehicle();
  40. // Create the UI content
  41. SimpleCreateInstructionsWithWasd("\nF5 to save scene, F7 to load");
  42. // Subscribe to necessary events
  43. SubscribeToEvents();
  44. }
  45. void SubscribeToEvents()
  46. {
  47. SubscribeToEvent<PostUpdateEvent>(e =>
  48. {
  49. if (vehicle == null)
  50. return;
  51. Node vehicleNode = vehicle.Node;
  52. // Physics update has completed. Position camera behind vehicle
  53. Quaternion dir = Quaternion.FromAxisAngle(Vector3.UnitY, vehicleNode.Rotation.YawAngle);
  54. dir = dir * Quaternion.FromAxisAngle(Vector3.UnitY, vehicle.Controls.Yaw);
  55. dir = dir * Quaternion.FromAxisAngle(Vector3.UnitX, vehicle.Controls.Pitch);
  56. Vector3 cameraTargetPos = vehicleNode.Position - (dir * new Vector3(0.0f, 0.0f, CameraDistance));
  57. Vector3 cameraStartPos = vehicleNode.Position;
  58. // and move it closer to the vehicle if something in between
  59. Ray cameraRay = new Ray(cameraStartPos, cameraTargetPos - cameraStartPos);
  60. float cameraRayLength = (cameraTargetPos - cameraStartPos).Length;
  61. // Raycast camera against static objects (physics collision mask 2)
  62. var query = new RayOctreeQuery(cameraRay, RayQueryLevel.RAY_TRIANGLE, cameraRayLength, Constants.DRAWABLE_ANY, 2);
  63. PhysicsRaycastResult result = new PhysicsRaycastResult();
  64. scene.GetComponent<PhysicsWorld>().RaycastSingle(ref result, cameraRay, cameraRayLength, 2);
  65. if (result.Body != null)
  66. {
  67. cameraTargetPos = cameraStartPos + cameraRay.Direction * (result.Distance - 0.5f);
  68. }
  69. CameraNode.Position = cameraTargetPos;
  70. CameraNode.Rotation = dir;
  71. });
  72. scene.GetComponent<PhysicsWorld>().SubscribeToEvent<PhysicsPreStepEvent>(e => vehicle?.FixedUpdate(e.TimeStep));
  73. }
  74. protected override void Update(float timeStep)
  75. {
  76. Input input = GetSubsystem<Input>();
  77. if (vehicle != null)
  78. {
  79. // Get movement controls and assign them to the vehicle component. If UI has a focused element, clear controls
  80. vehicle.Controls.Set(Vehicle.CtrlForward, input.GetKeyDown(Constants.KEY_W));
  81. vehicle.Controls.Set(Vehicle.CtrlBack, input.GetKeyDown(Constants.KEY_S));
  82. vehicle.Controls.Set(Vehicle.CtrlLeft, input.GetKeyDown(Constants.KEY_A));
  83. vehicle.Controls.Set(Vehicle.CtrlRight, input.GetKeyDown(Constants.KEY_D));
  84. // Add yaw & pitch from the mouse motion or touch input. Used only for the camera, does not affect motion
  85. if (TouchEnabled)
  86. {
  87. for (uint i = 0; i < input.NumTouches; ++i)
  88. {
  89. /*
  90. TouchState state = input.GetTouch(i);
  91. Camera camera = CameraNode.GetComponent<Camera>();
  92. if (camera == null)
  93. return;
  94. var graphics = Graphics;
  95. vehicle.Controls.Yaw += TouchSensitivity * camera.Fov / graphics.Height * state.Delta.X;
  96. vehicle.Controls.Pitch += TouchSensitivity * camera.Fov / graphics.Height * state.Delta.Y;
  97. */
  98. }
  99. }
  100. else
  101. {
  102. vehicle.Controls.Yaw += (float)input.MouseMoveX * Vehicle.YawSensitivity;
  103. vehicle.Controls.Pitch += (float)input.MouseMoveY * Vehicle.YawSensitivity;
  104. }
  105. // Limit pitch
  106. vehicle.Controls.Pitch = MathHelper.Clamp(vehicle.Controls.Pitch, 0.0f, 80.0f);
  107. // Check for loading / saving the scene
  108. /*
  109. if (input.GetKeyPress(Key.F5))
  110. {
  111. scene.SaveXml(FileSystem.ProgramDir + "Data/Scenes/VehicleDemo.xml");
  112. }
  113. if (input.GetKeyPress(Key.F7))
  114. {
  115. scene.LoadXml(FileSystem.ProgramDir + "Data/Scenes/VehicleDemo.xml");
  116. // After loading we have to reacquire the weak pointer to the Vehicle component, as it has been recreated
  117. // Simply find the vehicle's scene node by name as there's only one of them
  118. Node vehicleNode = scene.GetChild("Vehicle", true);
  119. if (vehicleNode != null)
  120. vehicle = vehicleNode.GetComponent<Vehicle>();
  121. }
  122. */
  123. }
  124. else
  125. vehicle.Controls.Set(Vehicle.CtrlForward | Vehicle.CtrlBack | Vehicle.CtrlLeft | Vehicle.CtrlRight, false);
  126. }
  127. void CreateVehicle()
  128. {
  129. Node vehicleNode = scene.CreateChild("Vehicle");
  130. vehicleNode.Position = (new Vector3(0.0f, 5.0f, 0.0f));
  131. // Create the vehicle logic component
  132. vehicle = new Vehicle();
  133. vehicleNode.AddComponent(vehicle);
  134. // Create the rendering and physics components
  135. vehicle.Init();
  136. }
  137. void CreateScene()
  138. {
  139. var cache = GetSubsystem<ResourceCache>();
  140. scene = new Scene();
  141. // Create scene subsystem components
  142. scene.CreateComponent<Octree>();
  143. scene.CreateComponent<PhysicsWorld>();
  144. // Create camera and define viewport. We will be doing load / save, so it's convenient to create the camera outside the scene,
  145. // so that it won't be destroyed and recreated, and we don't have to redefine the viewport on load
  146. CameraNode = new Node();
  147. Camera camera = CameraNode.CreateComponent<Camera>();
  148. camera.FarClip = 500.0f;
  149. GetSubsystem<Renderer>().SetViewport(0, new Viewport(scene, camera));
  150. // Create static scene content. First create a zone for ambient lighting and fog control
  151. Node zoneNode = scene.CreateChild("Zone");
  152. Zone zone = zoneNode.CreateComponent<Zone>();
  153. zone.AmbientColor = new Color(0.15f, 0.15f, 0.15f);
  154. zone.FogColor = new Color(0.5f, 0.5f, 0.7f);
  155. zone.FogStart = 300.0f;
  156. zone.FogEnd = 500.0f;
  157. zone.SetBoundingBox(new BoundingBox(-2000.0f, 2000.0f));
  158. // Create a directional light with cascaded shadow mapping
  159. Node lightNode = scene.CreateChild("DirectionalLight");
  160. lightNode.SetDirection(new Vector3(0.3f, -0.5f, 0.425f));
  161. Light light = lightNode.CreateComponent<Light>();
  162. light.LightType = LightType.LIGHT_DIRECTIONAL;
  163. light.CastShadows = true;
  164. light.ShadowBias = new BiasParameters(0.00025f, 0.5f);
  165. light.ShadowCascade = new CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
  166. light.SpecularIntensity = 0.5f;
  167. // Create heightmap terrain with collision
  168. Node terrainNode = scene.CreateChild("Terrain");
  169. terrainNode.Position = (Vector3.Zero);
  170. Terrain terrain = terrainNode.CreateComponent<Terrain>();
  171. terrain.PatchSize = 64;
  172. terrain.Spacing = new Vector3(2.0f, 0.1f, 2.0f); // Spacing between vertices and vertical resolution of the height map
  173. terrain.Smoothing = true;
  174. terrain.SetHeightMap(cache.Get<Image>("Textures/HeightMap.png"));
  175. terrain.Material = cache.Get<Material>("Materials/Terrain.xml");
  176. // The terrain consists of large triangles, which fits well for occlusion rendering, as a hill can occlude all
  177. // terrain patches and other objects behind it
  178. terrain.Occluder = true;
  179. RigidBody body = terrainNode.CreateComponent<RigidBody>();
  180. body.CollisionLayer = 2; // Use layer bitmask 2 for static geometry
  181. CollisionShape shape = terrainNode.CreateComponent<CollisionShape>();
  182. shape.SetTerrain(0);
  183. // Create 1000 mushrooms in the terrain. Always face outward along the terrain normal
  184. const uint numMushrooms = 1000;
  185. for (uint i = 0; i < numMushrooms; ++i)
  186. {
  187. Node objectNode = scene.CreateChild("Mushroom");
  188. Vector3 position = new Vector3(NextRandom(2000.0f) - 1000.0f, 0.0f, NextRandom(2000.0f) - 1000.0f);
  189. position.Y = terrain.GetHeight(position) - 0.1f;
  190. objectNode.Position = (position);
  191. // Create a rotation quaternion from up vector to terrain normal
  192. objectNode.Rotation = Quaternion.FromRotationTo(Vector3.UnitY, terrain.GetNormal(position));
  193. objectNode.SetScale(3.0f);
  194. StaticModel sm = objectNode.CreateComponent<StaticModel>();
  195. sm.Model = (cache.Get<Model>("Models/Mushroom.mdl"));
  196. sm.SetMaterial(cache.Get<Material>("Materials/Mushroom.xml"));
  197. sm.CastShadows = true;
  198. body = objectNode.CreateComponent<RigidBody>();
  199. body.CollisionLayer = 2;
  200. shape = objectNode.CreateComponent<CollisionShape>();
  201. shape.SetTriangleMesh(sm.Model, 0, Vector3.One, Vector3.Zero, Quaternion.Identity);
  202. }
  203. }
  204. }
  205. }