Browse Source

Adding Vehicle Demo

Josh Engebretson 9 years ago
parent
commit
b6c34939c1

+ 196 - 0
FeatureExamples/Resources/Components/Vehicle.cs

@@ -0,0 +1,196 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2015 Xamarin Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+using System;
+using AtomicEngine;
+
+namespace FeatureExamples
+{
+    /// <summary>
+    /// Vehicle component, responsible for physical movement according to controls.
+    /// </summary>
+    public class Vehicle : CSComponent
+    {
+        public const int CtrlForward = 1;
+        public const int CtrlBack = 2;
+        public const int CtrlLeft = 4;
+        public const int CtrlRight = 8;
+
+        public const float YawSensitivity = 0.1f;
+        public const float EnginePower = 10.0f;
+        public const float DownForce = 10.0f;
+        public const float MaxWheelAngle = 22.5f;
+
+        // Movement controls.
+        public Controls Controls { get; set; } = new Controls();
+
+        // Wheel scene nodes.
+        Node frontLeft;
+        Node frontRight;
+        Node rearLeft;
+        Node rearRight;
+
+        // Steering axle constraints.
+        Constraint frontLeftAxis;
+        Constraint frontRightAxis;
+
+        // Hull and wheel rigid bodies.
+        RigidBody hullBody;
+        RigidBody frontLeftBody;
+        RigidBody frontRightBody;
+        RigidBody rearLeftBody;
+        RigidBody rearRightBody;
+
+        // IDs of the wheel scene nodes for serialization.
+        uint frontLeftId;
+        uint frontRightId;
+        uint rearLeftId;
+        uint rearRightId;
+
+        /// Current left/right steering amount (-1 to 1.)
+        float steering;
+
+
+        public void FixedUpdate(float timeStep)
+        {
+            float newSteering = 0.0f;
+            float accelerator = 0.0f;
+
+            // Read controls
+            if (Controls.IsDown(CtrlLeft))
+                newSteering = -1.0f;
+            if (Controls.IsDown(CtrlRight))
+                newSteering = 1.0f;
+            if (Controls.IsDown(CtrlForward))
+                accelerator = 1.0f;
+            if (Controls.IsDown(CtrlBack))
+                accelerator = -0.5f;
+
+            // When steering, wake up the wheel rigidbodies so that their orientation is updated
+            if (newSteering != 0.0f)
+            {
+                frontLeftBody.Activate();
+                frontRightBody.Activate();
+                steering = steering * 0.95f + newSteering * 0.05f;
+            }
+            else
+                steering = steering * 0.8f + newSteering * 0.2f;
+
+            // Set front wheel angles
+            Quaternion steeringRot = new Quaternion(0, steering * MaxWheelAngle, 0);
+            frontLeftAxis.SetOtherAxis(steeringRot * new Vector3(-1f, 0f, 0f));
+            frontRightAxis.SetOtherAxis(steeringRot * Vector3.UnitX);
+
+            Quaternion hullRot = hullBody.Rotation;
+            if (accelerator != 0.0f)
+            {
+                // Torques are applied in world space, so need to take the vehicle & wheel rotation into account
+                Vector3 torqueVec = new Vector3(EnginePower * accelerator, 0.0f, 0.0f);
+
+                frontLeftBody.ApplyTorque(hullRot * steeringRot * torqueVec);
+                frontRightBody.ApplyTorque(hullRot * steeringRot * torqueVec);
+                rearLeftBody.ApplyTorque(hullRot * torqueVec);
+                rearRightBody.ApplyTorque(hullRot * torqueVec);
+            }
+
+            // Apply downforce proportional to velocity
+            Vector3 localVelocity = Quaternion.Invert(hullRot) * hullBody.LinearVelocity;
+            hullBody.ApplyForce(hullRot * new Vector3(0f, -1f, 0f) * Math.Abs(localVelocity.Z) * DownForce);
+        }
+
+        public void Init()
+        {
+            var cache = GetSubsystem<ResourceCache>();
+
+            // This function is called only from the main program when initially creating the vehicle, not on scene load
+            var node = Node;
+            StaticModel hullObject = node.CreateComponent<StaticModel>();
+            hullBody = node.CreateComponent<RigidBody>();
+            CollisionShape hullShape = node.CreateComponent<CollisionShape>();
+
+            node.Scale = new Vector3(1.5f, 1.0f, 3.0f);
+            hullObject.Model = cache.Get<Model>("Models/Box.mdl");
+            hullObject.SetMaterial(cache.Get<Material>("Materials/Stone.xml"));
+            hullObject.CastShadows = true;
+            hullShape.SetBox(Vector3.One, Vector3.Zero, Quaternion.Identity);
+            hullBody.Mass = 4.0f;
+            hullBody.LinearDamping = 0.2f; // Some air resistance
+            hullBody.AngularDamping = 0.5f;
+            hullBody.CollisionLayer = 1;
+
+            InitWheel("FrontLeft", new Vector3(-0.6f, -0.4f, 0.3f), out frontLeft, out frontLeftId);
+            InitWheel("FrontRight", new Vector3(0.6f, -0.4f, 0.3f), out frontRight, out frontRightId);
+            InitWheel("RearLeft", new Vector3(-0.6f, -0.4f, -0.3f), out rearLeft, out rearLeftId);
+            InitWheel("RearRight", new Vector3(0.6f, -0.4f, -0.3f), out rearRight, out rearRightId);
+
+            GetWheelComponents();
+        }
+
+        void InitWheel(string name, Vector3 offset, out Node wheelNode, out uint wheelNodeId)
+        {
+            var cache = GetSubsystem<ResourceCache>();
+
+            // Note: do not parent the wheel to the hull scene node. Instead create it on the root level and let the physics
+            // constraint keep it together
+            wheelNode = Scene.CreateChild(name);
+            wheelNode.Position = Node.LocalToWorld(offset);
+            wheelNode.Rotation = Node.Rotation * (offset.X >= 0.0 ? new Quaternion(0.0f, 0.0f, -90.0f) : new Quaternion(0.0f, 0.0f, 90.0f));
+            wheelNode.Scale = new Vector3(0.8f, 0.5f, 0.8f);
+            // Remember the ID for serialization
+            wheelNodeId = wheelNode.ID;
+
+            StaticModel wheelObject = wheelNode.CreateComponent<StaticModel>();
+            RigidBody wheelBody = wheelNode.CreateComponent<RigidBody>();
+            CollisionShape wheelShape = wheelNode.CreateComponent<CollisionShape>();
+            Constraint wheelConstraint = wheelNode.CreateComponent<Constraint>();
+
+            wheelObject.Model = (cache.Get<Model>("Models/Cylinder.mdl"));
+            wheelObject.SetMaterial(cache.Get<Material>("Materials/Stone.xml"));
+            wheelObject.CastShadows = true;
+            wheelShape.SetSphere(1.0f, Vector3.Zero, Quaternion.Identity);
+            wheelBody.Friction = (1.0f);
+            wheelBody.Mass = 1.0f;
+            wheelBody.LinearDamping = 0.2f; // Some air resistance
+            wheelBody.AngularDamping = 0.75f; // Could also use rolling friction
+            wheelBody.CollisionLayer = 1;
+            wheelConstraint.ConstraintType = ConstraintType.CONSTRAINT_HINGE;
+            wheelConstraint.OtherBody = GetComponent<RigidBody>(); // Connect to the hull body
+            wheelConstraint.SetWorldPosition(wheelNode.Position); // Set constraint's both ends at wheel's location
+            wheelConstraint.SetAxis(Vector3.UnitY); // Wheel rotates around its local Y-axis
+            wheelConstraint.SetOtherAxis(offset.X >= 0.0 ? Vector3.UnitX : new Vector3(-1f, 0f, 0f)); // Wheel's hull axis points either left or right
+            wheelConstraint.LowLimit = new Vector2(-180.0f, 0.0f); // Let the wheel rotate freely around the axis
+            wheelConstraint.HighLimit = new Vector2(180.0f, 0.0f);
+            wheelConstraint.DisableCollision = true; // Let the wheel intersect the vehicle hull
+        }
+
+        void GetWheelComponents()
+        {
+            frontLeftAxis = frontLeft.GetComponent<Constraint>();
+            frontRightAxis = frontRight.GetComponent<Constraint>();
+            frontLeftBody = frontLeft.GetComponent<RigidBody>();
+            frontRightBody = frontRight.GetComponent<RigidBody>();
+            rearLeftBody = rearLeft.GetComponent<RigidBody>();
+            rearRightBody = rearRight.GetComponent<RigidBody>();
+        }
+    }
+}

+ 5 - 0
FeatureExamples/Resources/Components/Vehicle.cs.asset

@@ -0,0 +1,5 @@
+{
+	"version": 1,
+	"guid": "7b057ce92d73e2f39ccd024348bf0025",
+	"CSharpImporter": null
+}

+ 246 - 0
FeatureExamples/Resources/Scripts/19_VehicleDemo.cs

@@ -0,0 +1,246 @@
+//
+// Copyright (c) 2008-2015 the Urho3D project.
+// Copyright (c) 2015 Xamarin Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+using AtomicEngine;
+
+namespace FeatureExamples
+{
+    public class VehicleDemo : Sample
+    {
+        Scene scene;
+        Vehicle vehicle;
+
+        const float CameraDistance = 10.0f;
+
+        public VehicleDemo() : base() { }
+
+        public override void Start()
+        {
+            base.Start();
+
+            // Create static scene content
+            CreateScene();
+
+            // Create the controllable vehicle
+            CreateVehicle();
+
+            // Create the UI content
+            SimpleCreateInstructionsWithWasd("\nF5 to save scene, F7 to load");
+
+            // Subscribe to necessary events
+            SubscribeToEvents();
+        }
+
+        void SubscribeToEvents()
+        {
+            SubscribeToEvent<PostUpdateEvent>(e =>
+            {
+                if (vehicle == null)
+                    return;
+
+                Node vehicleNode = vehicle.Node;
+
+                // Physics update has completed. Position camera behind vehicle
+                Quaternion dir = Quaternion.FromAxisAngle(Vector3.UnitY, vehicleNode.Rotation.YawAngle);
+
+                dir = dir * Quaternion.FromAxisAngle(Vector3.UnitY, vehicle.Controls.Yaw);
+                dir = dir * Quaternion.FromAxisAngle(Vector3.UnitX, vehicle.Controls.Pitch);
+
+                Vector3 cameraTargetPos = vehicleNode.Position - (dir * new Vector3(0.0f, 0.0f, CameraDistance));
+                Vector3 cameraStartPos = vehicleNode.Position;
+
+
+                // and move it closer to the vehicle if something in between
+                Ray cameraRay = new Ray(cameraStartPos, cameraTargetPos - cameraStartPos);
+                float cameraRayLength = (cameraTargetPos - cameraStartPos).Length;
+
+                // Raycast camera against static objects (physics collision mask 2)
+                var query = new RayOctreeQuery(cameraRay, RayQueryLevel.RAY_TRIANGLE, cameraRayLength, Constants.DRAWABLE_ANY, 2);
+
+                PhysicsRaycastResult result = new PhysicsRaycastResult();
+
+                scene.GetComponent<PhysicsWorld>().RaycastSingle(ref result, cameraRay, cameraRayLength, 2);
+                if (result.Body != null)
+                {
+                    cameraTargetPos = cameraStartPos + cameraRay.Direction * (result.Distance - 0.5f);
+                }
+
+                CameraNode.Position = cameraTargetPos;
+                CameraNode.Rotation = dir;
+            });
+
+            scene.GetComponent<PhysicsWorld>().SubscribeToEvent<PhysicsPreStepEvent>(e => vehicle?.FixedUpdate(e.TimeStep));
+        }
+
+        protected override void Update(float timeStep)
+        {
+            Input input = GetSubsystem<Input>();
+
+            if (vehicle != null)
+            {
+                // Get movement controls and assign them to the vehicle component. If UI has a focused element, clear controls
+                vehicle.Controls.Set(Vehicle.CtrlForward, input.GetKeyDown(Constants.KEY_W));
+                vehicle.Controls.Set(Vehicle.CtrlBack, input.GetKeyDown(Constants.KEY_S));
+                vehicle.Controls.Set(Vehicle.CtrlLeft, input.GetKeyDown(Constants.KEY_A));
+                vehicle.Controls.Set(Vehicle.CtrlRight, input.GetKeyDown(Constants.KEY_D));
+
+                // Add yaw & pitch from the mouse motion or touch input. Used only for the camera, does not affect motion
+                if (TouchEnabled)
+                {
+                    for (uint i = 0; i < input.NumTouches; ++i)
+                    {
+                        /*
+                        TouchState state = input.GetTouch(i);
+                        Camera camera = CameraNode.GetComponent<Camera>();
+                        if (camera == null)
+                            return;
+
+                        var graphics = Graphics;
+                        vehicle.Controls.Yaw += TouchSensitivity * camera.Fov / graphics.Height * state.Delta.X;
+                        vehicle.Controls.Pitch += TouchSensitivity * camera.Fov / graphics.Height * state.Delta.Y;
+                        */
+                    }
+                }
+                else
+                {
+                    vehicle.Controls.Yaw += (float)input.MouseMoveX * Vehicle.YawSensitivity;
+                    vehicle.Controls.Pitch += (float)input.MouseMoveY * Vehicle.YawSensitivity;
+                }
+
+                // Limit pitch
+                vehicle.Controls.Pitch = MathHelper.Clamp(vehicle.Controls.Pitch, 0.0f, 80.0f);
+
+                // Check for loading / saving the scene
+
+                /*
+                    if (input.GetKeyPress(Key.F5))
+                    {
+                        scene.SaveXml(FileSystem.ProgramDir + "Data/Scenes/VehicleDemo.xml");
+                    }
+                    if (input.GetKeyPress(Key.F7))
+                    {
+                        scene.LoadXml(FileSystem.ProgramDir + "Data/Scenes/VehicleDemo.xml");
+                        // After loading we have to reacquire the weak pointer to the Vehicle component, as it has been recreated
+                        // Simply find the vehicle's scene node by name as there's only one of them
+                        Node vehicleNode = scene.GetChild("Vehicle", true);
+                        if (vehicleNode != null)
+                            vehicle = vehicleNode.GetComponent<Vehicle>();
+                    }
+                    */
+            }
+            else
+                vehicle.Controls.Set(Vehicle.CtrlForward | Vehicle.CtrlBack | Vehicle.CtrlLeft | Vehicle.CtrlRight, false);
+
+        }
+
+        void CreateVehicle()
+        {
+            Node vehicleNode = scene.CreateChild("Vehicle");
+            vehicleNode.Position = (new Vector3(0.0f, 5.0f, 0.0f));
+
+            // Create the vehicle logic component
+            vehicle = new Vehicle();
+            vehicleNode.AddComponent(vehicle);
+            // Create the rendering and physics components
+            vehicle.Init();
+        }
+
+
+        void CreateScene()
+        {
+            var cache = GetSubsystem<ResourceCache>();
+
+            scene = new Scene();
+
+            // Create scene subsystem components
+            scene.CreateComponent<Octree>();
+            scene.CreateComponent<PhysicsWorld>();
+
+            // Create camera and define viewport. We will be doing load / save, so it's convenient to create the camera outside the scene,
+            // so that it won't be destroyed and recreated, and we don't have to redefine the viewport on load
+            CameraNode = new Node();
+            Camera camera = CameraNode.CreateComponent<Camera>();
+            camera.FarClip = 500.0f;
+            GetSubsystem<Renderer>().SetViewport(0, new Viewport(scene, camera));
+
+            // Create static scene content. First create a zone for ambient lighting and fog control
+            Node zoneNode = scene.CreateChild("Zone");
+            Zone zone = zoneNode.CreateComponent<Zone>();
+            zone.AmbientColor = new Color(0.15f, 0.15f, 0.15f);
+            zone.FogColor = new Color(0.5f, 0.5f, 0.7f);
+            zone.FogStart = 300.0f;
+            zone.FogEnd = 500.0f;
+            zone.SetBoundingBox(new BoundingBox(-2000.0f, 2000.0f));
+
+            // Create a directional light with cascaded shadow mapping
+            Node lightNode = scene.CreateChild("DirectionalLight");
+            lightNode.SetDirection(new Vector3(0.3f, -0.5f, 0.425f));
+            Light light = lightNode.CreateComponent<Light>();
+            light.LightType = LightType.LIGHT_DIRECTIONAL;
+            light.CastShadows = true;
+            light.ShadowBias = new BiasParameters(0.00025f, 0.5f);
+            light.ShadowCascade = new CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f);
+            light.SpecularIntensity = 0.5f;
+
+            // Create heightmap terrain with collision
+            Node terrainNode = scene.CreateChild("Terrain");
+            terrainNode.Position = (Vector3.Zero);
+            Terrain terrain = terrainNode.CreateComponent<Terrain>();
+            terrain.PatchSize = 64;
+            terrain.Spacing = new Vector3(2.0f, 0.1f, 2.0f); // Spacing between vertices and vertical resolution of the height map
+            terrain.Smoothing = true;
+            terrain.SetHeightMap(cache.Get<Image>("Textures/HeightMap.png"));
+            terrain.Material = cache.Get<Material>("Materials/Terrain.xml");
+            // 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.Occluder = true;
+
+            RigidBody body = terrainNode.CreateComponent<RigidBody>();
+            body.CollisionLayer = 2; // Use layer bitmask 2 for static geometry
+            CollisionShape shape = terrainNode.CreateComponent<CollisionShape>();
+            shape.SetTerrain(0);
+
+            // Create 1000 mushrooms in the terrain. Always face outward along the terrain normal
+            const uint numMushrooms = 1000;
+            for (uint i = 0; i < numMushrooms; ++i)
+            {
+                Node objectNode = scene.CreateChild("Mushroom");
+                Vector3 position = new Vector3(NextRandom(2000.0f) - 1000.0f, 0.0f, NextRandom(2000.0f) - 1000.0f);
+                position.Y = terrain.GetHeight(position) - 0.1f;
+                objectNode.Position = (position);
+                // Create a rotation quaternion from up vector to terrain normal
+                objectNode.Rotation = Quaternion.FromRotationTo(Vector3.UnitY, terrain.GetNormal(position));
+                objectNode.SetScale(3.0f);
+                StaticModel sm = objectNode.CreateComponent<StaticModel>();
+                sm.Model = (cache.Get<Model>("Models/Mushroom.mdl"));
+                sm.SetMaterial(cache.Get<Material>("Materials/Mushroom.xml"));
+                sm.CastShadows = true;
+
+                body = objectNode.CreateComponent<RigidBody>();
+                body.CollisionLayer = 2;
+                shape = objectNode.CreateComponent<CollisionShape>();
+                shape.SetTriangleMesh(sm.Model, 0, Vector3.One, Vector3.Zero, Quaternion.Identity);
+            }
+        }
+    }
+}

+ 5 - 0
FeatureExamples/Resources/Scripts/19_VehicleDemo.cs.asset

@@ -0,0 +1,5 @@
+{
+	"version": 1,
+	"guid": "d58e9c4780c45265d5591be3b51bd5aa",
+	"CSharpImporter": null
+}