public void GetAxisAlignedBoundingBox() { Assert.AreEqual(new Aabb(new Vector3F(0, -100, 0), new Vector3F(1000, 0, 1000)), new HeightField().GetAabb(Pose.Identity)); Assert.AreEqual(new Aabb(new Vector3F(1000, -101, 2000), new Vector3F(1100, 5, 2200)), _field.GetAabb(Pose.Identity)); Assert.AreEqual(new Aabb(new Vector3F(0, -1, 0), new Vector3F(10, 5, 20)), new HeightField(0, 0, 10, 20, _samples, 3, 8) { Depth = 0 }.GetAabb(Pose.Identity)); // Now with pose. QuaternionF rotation = QuaternionF.CreateRotationX(0.2f); Pose pose = new Pose(new Vector3F(1, 1, 1), rotation); _field.Depth = 0; var box = new TransformedShape( new GeometricObject(new BoxShape(100, 6, 200), new Pose(new Vector3F(1050, 2, 2100)))); Assert.IsTrue(Vector3F.AreNumericallyEqual(box.GetAabb(pose).Minimum, _field.GetAabb(pose).Minimum)); _field.Depth = 4; box = new TransformedShape( new GeometricObject(new BoxShape(100, 10, 200), new Pose(new Vector3F(1000, 0, 2000)))); Assert.IsTrue(Vector3F.AreNumericallyEqual(box.GetAabb(pose).Minimum + rotation.Rotate(new Vector3F(50, 0, 100)), _field.GetAabb(pose).Minimum)); }
public void CreateRotationX() { float angle = 0.3f; QuaternionF q = QuaternionF.CreateRotation(Vector3F.UnitX, angle); QuaternionF qx = QuaternionF.CreateRotationX(angle); Assert.AreEqual(q, qx); }
public void PropertiesTest() { Assert.AreEqual(3, ((CircleShape)cs.ObjectA.Shape).Radius); Assert.AreEqual(new Vector3F(0, 5, 0), ((LineSegmentShape)cs.ObjectB.Shape).Start); Assert.AreEqual(new Vector3F(0, -5, 0), ((LineSegmentShape)cs.ObjectB.Shape).End); Assert.AreEqual(new Pose(new Vector3F(1, 0, 0), QuaternionF.CreateRotationX(ConstantsF.PiOver2)), cs.ObjectA.Pose); Assert.AreEqual(Pose.Identity, cs.ObjectB.Pose); }
public void HasRotationTest() { var srt = new SrtTransform(new Vector3F(1, 1, 1), QuaternionF.Identity, Vector3F.Zero); Assert.IsFalse(srt.HasRotation); srt.Rotation = QuaternionF.CreateRotationX(0.000001f); Assert.IsFalse(srt.HasRotation); srt.Rotation = QuaternionF.CreateRotationX(0.1f); Assert.IsTrue(srt.HasRotation); }
public void GetAxisAlignedBoundingBox() { Assert.AreEqual(new Aabb(), new RectangleShape().GetAabb(Pose.Identity)); Assert.AreEqual(new Aabb(new Vector3F(10, 100, -13), new Vector3F(10, 100, -13)), new RectangleShape().GetAabb(new Pose(new Vector3F(10, 100, -13), QuaternionF.CreateRotation(new Vector3F(1, 1, 1), 0.7f)))); Assert.AreEqual(new Aabb(new Vector3F(5, 90, 1000), new Vector3F(15, 110, 1000)), new RectangleShape(10, 20).GetAabb(new Pose(new Vector3F(10, 100, 1000), QuaternionF.Identity))); Assert.AreEqual(new Aabb(new Vector3F(5, 100, 990), new Vector3F(15, 100, 1010)), new RectangleShape(10, 20).GetAabb(new Pose(new Vector3F(10, 100, 1000), QuaternionF.CreateRotationX(ConstantsF.PiOver2)))); // TODO: Test complex rotations. }
public void ResetPose() { _currentYaw = _defaultYaw; _currentPitch = _defaultPitch; if (IsLoaded) { // Also update SceneNode.LastPose - this is required for some effect, like // object motion blur. CameraNode.SetLastPose(true); CameraNode.PoseWorld = new Pose( _defaultPosition, QuaternionF.CreateRotationY(_currentYaw) * QuaternionF.CreateRotationX(_currentPitch)); } }
public void Test1() { Pose p = Pose.Identity; Assert.AreEqual(Matrix44F.Identity, p.ToMatrix44F()); Assert.AreEqual(Matrix33F.Identity, p.Orientation); Assert.AreEqual(Vector3F.Zero, p.Position); p.Position = new Vector3F(1, 2, 3); p.Orientation = Matrix33F.CreateRotation(new Vector3F(3, -4, 9), 0.49f); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToWorldDirection(Vector3F.UnitX), 0), p * new Vector4F(1, 0, 0, 0))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToWorldDirection(Vector3F.UnitY), 0), p * new Vector4F(0, 1, 0, 0))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToWorldDirection(Vector3F.UnitZ), 0), p * new Vector4F(0, 0, 1, 0))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToWorldPosition(Vector3F.UnitX), 1), p * new Vector4F(1, 0, 0, 1))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToWorldPosition(Vector3F.UnitY), 1), p * new Vector4F(0, 1, 0, 1))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToWorldPosition(Vector3F.UnitZ), 1), p * new Vector4F(0, 0, 1, 1))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToLocalDirection(Vector3F.UnitX), 0), p.Inverse * new Vector4F(1, 0, 0, 0))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToLocalDirection(Vector3F.UnitY), 0), p.Inverse * new Vector4F(0, 1, 0, 0))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToLocalDirection(Vector3F.UnitZ), 0), p.Inverse * new Vector4F(0, 0, 1, 0))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToLocalPosition(Vector3F.UnitX), 1), p.Inverse * new Vector4F(1, 0, 0, 1))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToLocalPosition(Vector3F.UnitY), 1), p.Inverse * new Vector4F(0, 1, 0, 1))); Assert.IsTrue(Vector4F.AreNumericallyEqual(new Vector4F(p.ToLocalPosition(Vector3F.UnitZ), 1), p.Inverse * new Vector4F(0, 0, 1, 1))); Pose p2 = Pose.FromMatrix(new Matrix44F(p.Orientation, Vector3F.Zero)); Assert.IsTrue(Matrix33F.AreNumericallyEqual(p.Orientation, p2.Orientation)); Assert.IsTrue(Vector3F.AreNumericallyEqual(p2.Position, Vector3F.Zero)); Matrix44F m = p2; m.SetColumn(3, new Vector4F(p.Position, 1)); p2 = Pose.FromMatrix(m); Assert.IsTrue(Matrix33F.AreNumericallyEqual(p.Orientation, p2.Orientation)); Assert.AreEqual(p.Position, p2.Position); //Assert.IsTrue(Vector3F.AreNumericallyEqual(p.Position, p2.Position)); // Test other constructors. Assert.AreEqual(Vector3F.Zero, new Pose(QuaternionF.CreateRotationX(0.3f)).Position); Assert.AreEqual(Matrix33F.CreateRotationX(0.3f), new Pose(Matrix33F.CreateRotationX(0.3f)).Orientation); Assert.AreEqual(new Vector3F(1, 2, 3), new Pose(new Vector3F(1, 2, 3)).Position); Assert.AreEqual(Matrix33F.Identity, new Pose(new Vector3F(1, 2, 3)).Orientation); }
public override void Update(GameTime gameTime) { base.Update(gameTime); float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; // Change rotation angle. if (_moveArmDown) { _upperArmAngle -= 0.3f * deltaTime; } else { _upperArmAngle += 0.3f * deltaTime; } // Change direction when a certain angle is reached. if (Math.Abs(_upperArmAngle) > 0.5f) { _moveArmDown = !_moveArmDown; } // Get the bone index of the upper arm bone. var skeleton = _meshNode.Mesh.Skeleton; int upperArmIndex = skeleton.GetIndex("L_UpperArm"); // Define the desired bone transform. SrtTransform boneTransform = new SrtTransform(QuaternionF.CreateRotationY(_upperArmAngle)); // Set the new bone transform. var skeletonPose = _meshNode.SkeletonPose; skeletonPose.SetBoneTransform(upperArmIndex, boneTransform); // The class SkeletonHelper provides some useful extension methods. // One is SetBoneRotationAbsolute() which sets the orientation of a bone relative // to model space. int handIndex = skeleton.GetIndex("L_Hand"); SkeletonHelper.SetBoneRotationAbsolute(skeletonPose, handIndex, QuaternionF.CreateRotationX(ConstantsF.Pi)); }
public void ResetPose() { positionFilter.Reset(); orientationFilter.Reset(); positionFilter.RawValue[0] = _defaultPosition.X; positionFilter.RawValue[1] = _defaultPosition.Y; positionFilter.RawValue[2] = _defaultPosition.Z; orientationFilter.RawValue[0] = _defaultYaw; orientationFilter.RawValue[1] = _defaultPitch; if (IsLoaded) { // Also update SceneNode.LastPose - this is required for some effect, like // object motion blur. CameraNode.SetLastPose(true); CameraNode.PoseWorld = new Pose( _defaultPosition, QuaternionF.CreateRotationY(_defaultYaw) * QuaternionF.CreateRotationX(_defaultPitch)); } }
// This method shows how to safely compare vectors. private void CompareVectors() { var debugRenderer = GraphicsScreen.DebugRenderer2D; debugRenderer.DrawText("----- CompareVectors Example:"); // Define a vector. Vector3F v0 = new Vector3F(1000, 2000, 3000); // Define a rotation quaternion that rotates 360° around the x axis. QuaternionF rotation = QuaternionF.CreateRotationX(MathHelper.ToRadians(360)); // Rotate v0. Vector3F v1 = rotation.Rotate(v0); // The rotated vector v1 should be identical to v0 because a 360° rotation // should not change the vector. - But due to numerical errors v0 and v1 are // not equal. if (v0 == v1) { debugRenderer.DrawText("Vectors are equal."); } else { debugRenderer.DrawText("Vectors are not equal."); // This message is written. } // With Vector3F.AreNumericallyEqual() we can check if two vectors are equal // when we allow a small numeric tolerance. The tolerance that is applied is // Numeric.EpsilonF, e.g. 10^-5. if (Vector3F.AreNumericallyEqual(v0, v1)) { debugRenderer.DrawText("Vectors are numerically equal.\n"); // This message is written. } else { debugRenderer.DrawText("Vectors are not numerically equal.\n"); } }
// OnLoad() is called when the GameObject is added to the IGameObjectService. protected override void OnLoad() { var content = _services.GetInstance <ContentManager>(); _skyboxNode = new SkyboxNode(content.Load <TextureCube>("Sky2")) { Color = new Vector3F(SkyExposure), }; // The ambient light. var ambientLight = new AmbientLight { Color = new Vector3F(0.9f, 0.9f, 1f), HdrScale = 0.1f, Intensity = 0.5f, HemisphericAttenuation = 0.8f, }; _ambientLightNode = new LightNode(ambientLight) { Name = "Ambient", }; // The main directional light. var sunlight = new DirectionalLight { Color = new Vector3F(1, 0.9607844f, 0.9078432f), HdrScale = 0.4f, DiffuseIntensity = 1, SpecularIntensity = 1, }; _sunlightNode = new LightNode(sunlight) { Name = "Sunlight", Priority = 10, // This is the most important light. PoseWorld = new Pose(QuaternionF.CreateRotationY(-1.4f) * QuaternionF.CreateRotationX(-0.6f)), // This light uses Cascaded Shadow Mapping. Shadow = new CascadedShadow { #if XBOX PreferredSize = 512, #else PreferredSize = 1024, #endif Prefer16Bit = true, } }; // Add a lens flare for the key light. var lensFlare = new LensFlare(true) { QuerySize = 0.2f, Size = 0.2f, Name = "Sun Flare" }; var lensFlareTexture = content.Load <Texture2D>("LensFlare/LensFlares"); var circleTexture = new PackedTexture("Circle", lensFlareTexture, new Vector2F(0, 0), new Vector2F(0.25f, 0.5f)); var glowTexture = new PackedTexture("Glow", lensFlareTexture, new Vector2F(0.25f, 0), new Vector2F(0.25f, 0.5f)); var ringTexture = new PackedTexture("Ring", lensFlareTexture, new Vector2F(0.5f, 0), new Vector2F(0.25f, 0.5f)); var haloTexture = new PackedTexture("Halo", lensFlareTexture, new Vector2F(0.75f, 0), new Vector2F(0.25f, 0.5f)); var sunTexture = new PackedTexture("Sun", lensFlareTexture, new Vector2F(0, 0.5f), new Vector2F(0.25f, 0.5f)); var streaksTexture = new PackedTexture("Streaks", lensFlareTexture, new Vector2F(0.25f, 0.5f), new Vector2F(0.25f, 0.5f)); var flareTexture = new PackedTexture("Flare", lensFlareTexture, new Vector2F(0.5f, 0.5f), new Vector2F(0.25f, 0.5f)); lensFlare.Elements.Add(new LensFlareElement(-0.2f, 0.55f, 0.0f, new Color(175, 175, 255, 20), new Vector2F(0.5f, 0.5f), circleTexture)); lensFlare.Elements.Add(new LensFlareElement(0.0f, 0.9f, 0.0f, new Color(255, 255, 255, 255), new Vector2F(0.5f, 0.5f), sunTexture)); lensFlare.Elements.Add(new LensFlareElement(0.0f, 1.8f, 0.0f, new Color(255, 255, 255, 128), new Vector2F(0.5f, 0.5f), streaksTexture)); lensFlare.Elements.Add(new LensFlareElement(0.0f, 2.6f, 0.0f, new Color(255, 255, 200, 64), new Vector2F(0.5f, 0.5f), glowTexture)); lensFlare.Elements.Add(new LensFlareElement(0.5f, 0.12f, 0.0f, new Color(60, 60, 180, 35), new Vector2F(0.5f, 0.5f), circleTexture)); lensFlare.Elements.Add(new LensFlareElement(0.55f, 0.46f, 0.0f, new Color(100, 100, 200, 60), new Vector2F(0.5f, 0.5f), circleTexture)); lensFlare.Elements.Add(new LensFlareElement(0.6f, 0.17f, 0.0f, new Color(120, 120, 220, 40), new Vector2F(0.5f, 0.5f), circleTexture)); lensFlare.Elements.Add(new LensFlareElement(0.85f, 0.2f, 0.0f, new Color(60, 60, 255, 100), new Vector2F(0.5f, 0.5f), ringTexture)); lensFlare.Elements.Add(new LensFlareElement(1.5f, 0.2f, 0.0f, new Color(255, 60, 60, 130), new Vector2F(0.5f, 0.5f), flareTexture)); lensFlare.Elements.Add(new LensFlareElement(0.15f, 0.15f, 0.0f, new Color(255, 60, 60, 90), new Vector2F(0.5f, 0.5f), flareTexture)); lensFlare.Elements.Add(new LensFlareElement(1.3f, 0.6f, 0.0f, new Color(60, 60, 255, 180), new Vector2F(0.5f, 0.5f), haloTexture)); lensFlare.Elements.Add(new LensFlareElement(1.4f, 0.2f, 0.0f, new Color(220, 80, 80, 98), new Vector2F(0.5f, 0.5f), haloTexture)); lensFlare.Elements.Add(new LensFlareElement(1.5f, 0.1f, 0.0f, new Color(220, 80, 80, 85), new Vector2F(0.5f, 0.5f), circleTexture)); lensFlare.Elements.Add(new LensFlareElement(1.6f, 0.5f, 0.0f, new Color(60, 60, 255, 80), new Vector2F(0.5f, 0.5f), haloTexture)); lensFlare.Elements.Add(new LensFlareElement(1.8f, 0.3f, 0.0f, new Color(90, 60, 255, 110), new Vector2F(0.5f, 0.5f), ringTexture)); lensFlare.Elements.Add(new LensFlareElement(1.95f, 0.5f, 0.0f, new Color(60, 60, 255, 120), new Vector2F(0.5f, 0.5f), haloTexture)); lensFlare.Elements.Add(new LensFlareElement(2.0f, 0.15f, 0.0f, new Color(60, 60, 255, 85), new Vector2F(0.5f, 0.5f), circleTexture)); // Add lens flare as a child of the sunlight. var lensFlareNode = new LensFlareNode(lensFlare); _sunlightNode.Children = new SceneNodeCollection(); _sunlightNode.Children.Add(lensFlareNode); // Add scene nodes to scene graph. var scene = _services.GetInstance <IScene>(); scene.Children.Add(_skyboxNode); scene.Children.Add(_ambientLightNode); scene.Children.Add(_sunlightNode); }
public void TransformedShapeNonuniformScaleWithRotationNotSupported() { var s = new TransformedShape(new GeometricObject(new BoxShape(3, 2, 1), new Vector3F(0.7f, 0.8f, 0.9f), new Pose(new Vector3F(-1, 7, 4), QuaternionF.CreateRotationX(1)))); float m0; Vector3F com0; Matrix33F i0; MassHelper.GetMass(s, new Vector3F(2, 2.1f, 2.8f), 1, true, 0.001f, 10, out m0, out com0, out i0); }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- protected override void OnLoad() { // Add rigid bodies to simulation. var simulation = _services.GetInstance <Simulation>(); // We use a random number generator with a custom seed. RandomHelper.Random = new Random(123); // ----- Add a ground plane. AddBody(simulation, "GroundPlane", Pose.Identity, new PlaneShape(Vector3F.UnitY, 0), MotionType.Static); // ----- Create a small flying sphere. AddBody(simulation, "Sphere", new Pose(new Vector3F(0, 1f, 0)), new SphereShape(0.2f), MotionType.Static); // ----- Create small walls at the level boundary. AddBody(simulation, "WallLeft", new Pose(new Vector3F(-30, 1, 0)), new BoxShape(0.3f, 2, 60), MotionType.Static); AddBody(simulation, "WallRight", new Pose(new Vector3F(30, 1, 0)), new BoxShape(0.3f, 2, 60), MotionType.Static); AddBody(simulation, "WallFront", new Pose(new Vector3F(0, 1, -30)), new BoxShape(60, 2, 0.3f), MotionType.Static); AddBody(simulation, "WallBack", new Pose(new Vector3F(0, 1, 30)), new BoxShape(60, 2, 0.3f), MotionType.Static); // ----- Create a few bigger objects. // We position the boxes so that we have a few corners we can run into. Character controllers // should be stable when the user runs into corners. AddBody(simulation, "House0", new Pose(new Vector3F(10, 1, -10)), new BoxShape(8, 2, 8f), MotionType.Static); AddBody(simulation, "House1", new Pose(new Vector3F(13, 1, -4)), new BoxShape(2, 2, 4), MotionType.Static); AddBody(simulation, "House2", new Pose(new Vector3F(10, 2, -15), Matrix33F.CreateRotationY(-0.3f)), new BoxShape(8, 4, 2), MotionType.Static); // ----- Create stairs with increasing step height. // Each step is a box. With this object we can test if our character can climb up // stairs. The character controller has a step height limit. Increasing step heights // let us test if the step height limit works. float startHeight = 0; const float stepDepth = 1f; for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.05f; Vector3F position = new Vector3F(0, startHeight + stepHeight / 2, -2 - i * stepDepth); AddBody(simulation, "Step" + i, new Pose(position), new BoxShape(2, stepHeight, stepDepth), MotionType.Static); startHeight += stepHeight; } // ----- V obstacle to test if we get stuck. AddBody(simulation, "V0", new Pose(new Vector3F(-5.5f, 0, 10), QuaternionF.CreateRotationZ(0.2f)), new BoxShape(1f, 2f, 2), MotionType.Static); AddBody(simulation, "V1", new Pose(new Vector3F(-4, 0, 10), QuaternionF.CreateRotationZ(-0.2f)), new BoxShape(1f, 2f, 2), MotionType.Static); // ----- Create a height field. // Terrain that is uneven is best modeled with a height field. Height fields are faster // than general triangle meshes. // The height direction is the y direction. // The height field lies in the x/z plane. var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; // Create arbitrary height values. for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { if (x == 0 || z == 0 || x == 19 || z == 19) { // Set this boundary elements to a low height, so that the height field is connected // to the ground. samples[z * numberOfSamplesX + x] = -1; } else { // A sine/cosine function that creates some interesting waves. samples[z * numberOfSamplesX + x] = 1.0f + (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 1.0f); } } } var heightField = new HeightField(0, 0, 20, 20, samples, numberOfSamplesX, numberOfSamplesZ); AddBody(simulation, "HeightField", new Pose(new Vector3F(10, 0, 10)), heightField, MotionType.Static); // ----- Create rubble on the floor (small random objects on the floor). // Our character should be able to move over small bumps on the ground. for (int i = 0; i < 50; i++) { Vector3F position = new Vector3F(RandomHelper.Random.NextFloat(-5, 5), 0, RandomHelper.Random.NextFloat(10, 20)); QuaternionF orientation = RandomHelper.Random.NextQuaternionF(); Vector3F size = RandomHelper.Random.NextVector3F(0.05f, 0.8f); AddBody(simulation, "Stone" + i, new Pose(position, orientation), new BoxShape(size), MotionType.Static); } // ----- Create some slopes to see how our character performs on/under sloped surfaces. // Here we can test how the character controller behaves if the head touches an inclined // ceiling. AddBody(simulation, "SlopeGround", new Pose(new Vector3F(-2, 1.8f, -12), QuaternionF.CreateRotationX(0.4f)), new BoxShape(2, 0.5f, 10), MotionType.Static); AddBody(simulation, "SlopeRoof", new Pose(new Vector3F(-2, 5.6f, -12), QuaternionF.CreateRotationX(-0.4f)), new BoxShape(2, 0.5f, 10), MotionType.Static); // Create slopes with increasing tilt angles. // The character controller has a slope limit. Only flat slopes should be climbable. // Movement between slopes should be smooth. Vector3F slopePosition = new Vector3F(-17, -0.25f, 6); BoxShape slopeShape = new BoxShape(8, 0.5f, 5); for (int i = 1; i < 8; i++) { Matrix33F oldRotation = Matrix33F.CreateRotationX((i - 1) * MathHelper.ToRadians(10)); Matrix33F rotation = Matrix33F.CreateRotationX(i * MathHelper.ToRadians(10)); slopePosition += (oldRotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2 + (rotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2; AddBody(simulation, "Slope" + i, new Pose(slopePosition, rotation), slopeShape, MotionType.Static); } // Create slopes with decreasing tilt angles. slopePosition = new Vector3F(-8, -2, 5); slopeShape = new BoxShape(8f, 0.5f, 5); for (int i = 1; i < 8; i++) { Matrix33F oldRotation = Matrix33F.CreateRotationX(MathHelper.ToRadians(40) - (i - 1) * MathHelper.ToRadians(10)); Matrix33F rotation = Matrix33F.CreateRotationX(MathHelper.ToRadians(40) - i * MathHelper.ToRadians(10)); slopePosition += (oldRotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2 + (rotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2; Vector3F position = slopePosition - rotation * new Vector3F(0, slopeShape.WidthY / 2, 0); AddBody(simulation, "Slope2" + i, new Pose(position, rotation), slopeShape, MotionType.Static); } // ----- Create a slope with a wall on one side. // This objects let's us test how the character controller behaves while falling and // sliding along a vertical wall. (Run up the slope and then jump down while moving into // the wall.) AddBody(simulation, "LongSlope", new Pose(new Vector3F(-24, 3, -10), Matrix33F.CreateRotationX(0.4f)), new BoxShape(4, 5f, 30), MotionType.Static); AddBody(simulation, "LongSlopeWall", new Pose(new Vector3F(-26, 5, -10)), new BoxShape(0.5f, 10f, 25), MotionType.Static); // ----- Create a trigger object that represents a ladder. var ladder = AddBody(simulation, "Ladder", new Pose(new Vector3F(-25.7f, 5, 0)), new BoxShape(0.5f, 10f, 1), MotionType.Static); ladder.CollisionObject.Type = CollisionObjectType.Trigger; // ----- Create a mesh object to test walking on triangle meshes. // Normally, the mesh would be loaded from a file. Here, we make a composite shape and // let DigitalRune Geometry compute a mesh for it. Then we throw away the composite // shape and use only the mesh. (We do this to test triangle meshes. Using the composite // shape instead of the triangle mesh would be a lot faster.) CompositeShape compositeShape = new CompositeShape(); compositeShape.Children.Add(new GeometricObject(heightField, Pose.Identity)); compositeShape.Children.Add(new GeometricObject(new CylinderShape(1, 2), new Pose(new Vector3F(10, 1, 10)))); compositeShape.Children.Add(new GeometricObject(new SphereShape(3), new Pose(new Vector3F(15, 0, 15)))); compositeShape.Children.Add(new GeometricObject(new BoxShape(4, 4, 3), new Pose(new Vector3F(15, 1.5f, 5)))); compositeShape.Children.Add(new GeometricObject(new BoxShape(4, 4, 3), new Pose(new Vector3F(15, 1.5f, 0)))); ITriangleMesh mesh = compositeShape.GetMesh(0.01f, 3); TriangleMeshShape meshShape = new TriangleMeshShape(mesh); // Collision detection speed for triangle meshes can be improved by using a spatial // partition. Here, we assign an AabbTree to the triangle mesh shape. The tree is // built automatically when needed and it stores triangle indices (therefore the generic // parameter of the AabbTree is int). meshShape.Partition = new AabbTree <int>() { // The tree is automatically built using a mixed top-down/bottom-up approach. Bottom-up // building is slower but produces better trees. If the tree building takes too long, // we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; // Contact welding creates smoother contact normals - but it costs a bit of performance. meshShape.EnableContactWelding = true; AddBody(simulation, "Mesh", new Pose(new Vector3F(-30, 0, 10)), meshShape, MotionType.Static); // ----- Create a seesaw. var seesawBase = AddBody(simulation, "SeesawBase", new Pose(new Vector3F(5, 0.5f, 0)), new BoxShape(0.2f, 1, 1), MotionType.Static); var seesaw = AddBody(simulation, "Seesaw", new Pose(new Vector3F(5, 1.05f, 0)), new BoxShape(5, 0.1f, 1), MotionType.Dynamic); // Attach the seesaw to the base using a hinge joint. simulation.Constraints.Add(new HingeJoint { BodyA = seesaw, BodyB = seesawBase, AnchorPoseALocal = new Pose(new Vector3F(0, 0, 0), new Matrix33F(0, 0, -1, 0, 1, 0, 1, 0, 0)), AnchorPoseBLocal = new Pose(new Vector3F(0, 0.5f, 0), new Matrix33F(0, 0, -1, 0, 1, 0, 1, 0, 0)), CollisionEnabled = false, }); // ----- A platform that is moving up/down. _elevator = AddBody(simulation, "Elevator", new Pose(new Vector3F(5, -1f, 5)), new BoxShape(3, 1f, 3), MotionType.Kinematic); _elevator.LinearVelocity = new Vector3F(2, 2, 0); // ----- A platform that is moving sideways. _pusher = AddBody(simulation, "Pusher", new Pose(new Vector3F(15, 0.5f, 0)), new BoxShape(3, 1f, 3), MotionType.Kinematic); _pusher.LinearVelocity = new Vector3F(0, 0, 2); // ----- Create conveyor belt with two static boxes on the sides. AddBody(simulation, "ConveyorSide0", new Pose(new Vector3F(19, 0.25f, 0)), new BoxShape(0.8f, 0.5f, 8f), MotionType.Static); AddBody(simulation, "ConveyorSide1", new Pose(new Vector3F(21, 0.25f, 0)), new BoxShape(0.8f, 0.5f, 8f), MotionType.Static); // The conveyor belt is a simple box with a special material. var conveyorBelt = AddBody(simulation, "ConveyorBelt", new Pose(new Vector3F(20, 0.25f, 0)), new BoxShape(1f, 0.51f, 8f), MotionType.Static); UniformMaterial materialWithSurfaceMotion = new UniformMaterial("ConveyorBelt", true) // Important: The second parameter enables the surface { // motion. It has to be set to true in the constructor! SurfaceMotion = new Vector3F(0, 0, 1), // The surface motion relative to the object. }; conveyorBelt.Material = materialWithSurfaceMotion; // ----- Distribute a few dynamic spheres and boxes across the landscape. SphereShape sphereShape = new SphereShape(0.5f); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-15, 15); position.Y = 20; AddBody(simulation, "Sphere" + i, new Pose(position), sphereShape, MotionType.Dynamic); } BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-15, 15); position.Y = 20; AddBody(simulation, "Box" + i, new Pose(position), boxShape, MotionType.Dynamic); } }
public void CompressSrtKeyFrameAnimation() { var random = new Random(12345); float scaleThreshold = 0.1f; float rotationThreshold = 2; // [°] float translationThreshold = 0.2f; var srtKeyFrameAnimation = new SrtKeyFrameAnimation(); // Define a view important keyframes. var time0 = TimeSpan.FromTicks(100000); var value0 = new SrtTransform(Vector3F.One, QuaternionF.Identity, Vector3F.Zero); var time1 = TimeSpan.FromTicks(200000); var value1 = new SrtTransform(new Vector3F(2, 2, 2), QuaternionF.CreateRotationX(MathHelper.ToRadians(10)), new Vector3F(1, 1, 1)); var time2 = TimeSpan.FromTicks(400000); var value2 = new SrtTransform(new Vector3F(-1, -1, -1), QuaternionF.CreateRotationX(MathHelper.ToRadians(80)), new Vector3F(10, 10, 10)); var time3 = TimeSpan.FromTicks(500000); var value3 = new SrtTransform(new Vector3F(3, 3, 3), QuaternionF.CreateRotationX(MathHelper.ToRadians(-10)), new Vector3F(-2, -2, -2)); srtKeyFrameAnimation.KeyFrames.Add(new KeyFrame <SrtTransform>(time0, value0)); srtKeyFrameAnimation.KeyFrames.Add(new KeyFrame <SrtTransform>(time1, value1)); srtKeyFrameAnimation.KeyFrames.Add(new KeyFrame <SrtTransform>(time2, value2)); srtKeyFrameAnimation.KeyFrames.Add(new KeyFrame <SrtTransform>(time3, value3)); // Add random keyframes within tolerance. InsertRandomKeyFrames(random, srtKeyFrameAnimation, time0, time1, scaleThreshold, rotationThreshold, translationThreshold); InsertRandomKeyFrames(random, srtKeyFrameAnimation, time1, time2, scaleThreshold, rotationThreshold, translationThreshold); InsertRandomKeyFrames(random, srtKeyFrameAnimation, time2, time3, scaleThreshold, rotationThreshold, translationThreshold); // ---- Compress animation with tolerance. var srtAnimation = AnimationHelper.Compress(srtKeyFrameAnimation, scaleThreshold, rotationThreshold, translationThreshold); Assert.IsNotNull(srtAnimation); Assert.AreEqual(srtKeyFrameAnimation.GetTotalDuration(), srtAnimation.GetTotalDuration()); Assert.IsNotNull(srtAnimation.Scale); Assert.AreEqual(4, ((KeyFrameAnimation <Vector3F>)srtAnimation.Scale).KeyFrames.Count); Assert.AreEqual(4, ((KeyFrameAnimation <QuaternionF>)srtAnimation.Rotation).KeyFrames.Count); Assert.AreEqual(4, ((KeyFrameAnimation <Vector3F>)srtAnimation.Translation).KeyFrames.Count); var defaultSource = SrtTransform.Identity; var defaultTarget = SrtTransform.Identity; var result = new SrtTransform(); srtAnimation.GetValue(time0, ref defaultSource, ref defaultTarget, ref result); Assert.AreEqual(value0, result); srtAnimation.GetValue(time1, ref defaultSource, ref defaultTarget, ref result); Assert.AreEqual(value1, result); srtAnimation.GetValue(time2, ref defaultSource, ref defaultTarget, ref result); Assert.AreEqual(value2, result); srtAnimation.GetValue(time3, ref defaultSource, ref defaultTarget, ref result); Assert.AreEqual(value3, result); // Take a view samples. const int numberOfSamples = 10; long tickIncrement = (time3 - time0).Ticks / (numberOfSamples + 1); for (int i = 0; i < numberOfSamples; i++) { var time = TimeSpan.FromTicks(time0.Ticks + (i + 1) * tickIncrement); var valueRef = new SrtTransform(); srtKeyFrameAnimation.GetValue(time, ref defaultSource, ref defaultTarget, ref valueRef); var valueNew = new SrtTransform(); srtAnimation.GetValue(time, ref defaultSource, ref defaultTarget, ref valueNew); Assert.IsTrue((valueRef.Scale - valueNew.Scale).Length <= scaleThreshold); Assert.IsTrue(QuaternionF.GetAngle(valueRef.Rotation, valueNew.Rotation) <= MathHelper.ToRadians(rotationThreshold)); Assert.IsTrue((valueRef.Translation - valueNew.Translation).Length <= translationThreshold); } // ----- Compress animation with zero tolerance. srtAnimation = AnimationHelper.Compress(srtKeyFrameAnimation, 0, 0, 0); Assert.IsNotNull(srtAnimation); Assert.AreEqual(srtKeyFrameAnimation.GetTotalDuration(), srtAnimation.GetTotalDuration()); Assert.IsNotNull(srtAnimation.Scale); Assert.AreEqual(srtKeyFrameAnimation.KeyFrames.Count, ((KeyFrameAnimation <Vector3F>)srtAnimation.Scale).KeyFrames.Count); Assert.AreEqual(srtKeyFrameAnimation.KeyFrames.Count, ((KeyFrameAnimation <QuaternionF>)srtAnimation.Rotation).KeyFrames.Count); Assert.AreEqual(srtKeyFrameAnimation.KeyFrames.Count, ((KeyFrameAnimation <Vector3F>)srtAnimation.Translation).KeyFrames.Count); // Take a view samples. for (int i = 0; i < numberOfSamples; i++) { var time = TimeSpan.FromTicks(time0.Ticks + (i + 1) * tickIncrement); var valueRef = new SrtTransform(); srtKeyFrameAnimation.GetValue(time, ref defaultSource, ref defaultTarget, ref valueRef); var valueNew = new SrtTransform(); srtAnimation.GetValue(time, ref defaultSource, ref defaultTarget, ref valueNew); Assert.IsTrue(SrtTransform.AreNumericallyEqual(valueRef, valueNew)); } }
/// <summary> /// Creates a <see cref="Ragdoll"/> for an Xbox LIVE Avatar. (Only available on Xbox 360.) /// </summary> /// <param name="skeleton">The skeleton of the Xbox LIVE Avatar.</param> /// <param name="simulation">The simulation.</param> /// <returns>The avatar ragdoll.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="skeleton"/> or <paramref name="simulation"/> is /// <see langword="null"/>. /// </exception> /// <remarks> /// This method is available only in the Xbox 360 build of the /// DigitalRune.Physics.Specialized.dll. /// </remarks> public static Ragdoll CreateAvatarRagdoll(Skeleton skeleton, Simulation simulation) { if (skeleton == null) { throw new ArgumentNullException("skeleton"); } if (simulation == null) { throw new ArgumentNullException("simulation"); } var ragdoll = new Ragdoll(); // The lists ragdoll.Bodies, ragdoll.BodyOffsets and _motors contain one entry per bone - even if there // is no RigidBody for this bone. - This wastes memory but simplifies the code. for (int i = 0; i < AvatarRenderer.BoneCount; i++) { ragdoll.Bodies.Add(null); ragdoll.BodyOffsets.Add(Pose.Identity); ragdoll.Joints.Add(null); ragdoll.Limits.Add(null); ragdoll.Motors.Add(null); } // ----- Create bodies. // We use the same mass for all bodies. This is not physically correct but it makes the // simulation more stable, for several reasons: // - It is better to avoid large mass differences. Therefore, all limbs have the same mass. // - Capsule shapes have a low inertia value about their height axis. This causes instability // and it is better to use larger inertia values. var massFrame = MassFrame.FromShapeAndMass(new SphereShape(0.2f), Vector3F.One, 4, 0.1f, 1); // Use standard material. var material = new UniformMaterial(); // Create rigid bodies for the important bones. The shapes have been manually adapted to // produce useful results for thin and overweight avatars. // Without offset, the bodies are centered at the joint. ragdoll.BodyOffsets stores an offset pose // for each body. Instead, we could use TransformedShape but we can easily handle that // ourselves. // The collar bones are special, they use dummy shapes and are only used to connect the // shoulder bones. ragdoll.Bodies[(int)AvatarBone.Root] = new RigidBody(new BoxShape(0.22f, 0.16f, 0.16f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.Root] = new Pose(new Vector3F(0, -0.08f, -0.01f), QuaternionF.CreateRotationX(-0.0f)); ragdoll.Bodies[(int)AvatarBone.BackLower] = new RigidBody(new BoxShape(0.22f, 0.16f, 0.16f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.BackLower] = new Pose(new Vector3F(0, 0.08f, -0.01f), QuaternionF.CreateRotationX(-0.0f)); ragdoll.Bodies[(int)AvatarBone.BackUpper] = new RigidBody(new BoxShape(0.22f, 0.16f, 0.16f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.BackUpper] = new Pose(new Vector3F(0, 0.08f, -0.01f), QuaternionF.CreateRotationX(-0.1f)); ragdoll.Bodies[(int)AvatarBone.Neck] = new RigidBody(new CapsuleShape(0.04f, 0.09f), massFrame, material); ragdoll.Bodies[(int)AvatarBone.Head] = new RigidBody(new SphereShape(0.15f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.Head] = new Pose(new Vector3F(0, 0.1f, 0)); ragdoll.Bodies[(int)AvatarBone.CollarLeft] = new RigidBody(Shape.Empty, massFrame, material); ragdoll.Bodies[(int)AvatarBone.CollarRight] = new RigidBody(Shape.Empty, massFrame, material); ragdoll.Bodies[(int)AvatarBone.ShoulderLeft] = new RigidBody(new CapsuleShape(0.04f, 0.25f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.ShoulderLeft] = new Pose(new Vector3F(0.08f, 0, -0.02f), QuaternionF.CreateRotationZ(ConstantsF.PiOver2)); ragdoll.Bodies[(int)AvatarBone.ShoulderRight] = new RigidBody(new CapsuleShape(0.04f, 0.25f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.ShoulderRight] = new Pose(new Vector3F(-0.08f, 0, -0.02f), QuaternionF.CreateRotationZ(ConstantsF.PiOver2)); ragdoll.Bodies[(int)AvatarBone.ElbowLeft] = new RigidBody(new CapsuleShape(0.04f, 0.21f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.ElbowLeft] = new Pose(new Vector3F(0.06f, 0, -0.02f), QuaternionF.CreateRotationZ(ConstantsF.PiOver2)); ragdoll.Bodies[(int)AvatarBone.ElbowRight] = new RigidBody(new CapsuleShape(0.04f, 0.21f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.ElbowRight] = new Pose(new Vector3F(-0.06f, 0, -0.02f), QuaternionF.CreateRotationZ(ConstantsF.PiOver2)); ragdoll.Bodies[(int)AvatarBone.WristLeft] = new RigidBody(new BoxShape(0.1f, 0.04f, 0.1f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.WristLeft] = new Pose(new Vector3F(0.06f, -0.02f, -0.01f), QuaternionF.CreateRotationZ(0.0f)); ragdoll.Bodies[(int)AvatarBone.WristRight] = new RigidBody(new BoxShape(0.1f, 0.04f, 0.1f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.WristRight] = new Pose(new Vector3F(-0.06f, -0.02f, -0.01f), QuaternionF.CreateRotationZ(0.0f)); ragdoll.Bodies[(int)AvatarBone.HipLeft] = new RigidBody(new CapsuleShape(0.06f, 0.34f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.HipLeft] = new Pose(new Vector3F(0, -0.14f, -0.02f), QuaternionF.CreateRotationX(0.1f)); ragdoll.Bodies[(int)AvatarBone.HipRight] = new RigidBody(new CapsuleShape(0.06f, 0.34f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.HipRight] = new Pose(new Vector3F(0, -0.14f, -0.02f), QuaternionF.CreateRotationX(0.1f)); ragdoll.Bodies[(int)AvatarBone.KneeLeft] = new RigidBody(new CapsuleShape(0.06f, 0.36f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.KneeLeft] = new Pose(new Vector3F(0, -0.18f, -0.04f), QuaternionF.CreateRotationX(0.1f)); ragdoll.Bodies[(int)AvatarBone.KneeRight] = new RigidBody(new CapsuleShape(0.06f, 0.36f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.KneeRight] = new Pose(new Vector3F(0, -0.18f, -0.04f), QuaternionF.CreateRotationX(0.1f)); ragdoll.Bodies[(int)AvatarBone.AnkleLeft] = new RigidBody(new BoxShape(0.1f, 0.06f, 0.22f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.AnkleLeft] = new Pose(new Vector3F(0, -0.07f, 0.05f), QuaternionF.CreateRotationZ(0)); ragdoll.Bodies[(int)AvatarBone.AnkleRight] = new RigidBody(new BoxShape(0.1f, 0.06f, 0.22f), massFrame, material); ragdoll.BodyOffsets[(int)AvatarBone.AnkleRight] = new Pose(new Vector3F(0, -0.07f, 0.05f), QuaternionF.CreateRotationZ(0)); // ----- Add joint constraints. const float jointErrorReduction = 0.2f; const float jointSoftness = 0.0001f; AddJoint(ragdoll, skeleton, AvatarBone.Root, AvatarBone.BackLower, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.BackLower, AvatarBone.BackUpper, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.BackUpper, AvatarBone.Neck, 0.6f, 0.000001f); AddJoint(ragdoll, skeleton, AvatarBone.Neck, AvatarBone.Head, 0.6f, 0.000001f); AddJoint(ragdoll, skeleton, AvatarBone.BackUpper, AvatarBone.CollarLeft, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.BackUpper, AvatarBone.CollarRight, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.CollarLeft, AvatarBone.ShoulderLeft, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.CollarRight, AvatarBone.ShoulderRight, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.ShoulderLeft, AvatarBone.ElbowLeft, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.ShoulderRight, AvatarBone.ElbowRight, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.ElbowLeft, AvatarBone.WristLeft, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.ElbowRight, AvatarBone.WristRight, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.Root, AvatarBone.HipLeft, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.Root, AvatarBone.HipRight, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.HipLeft, AvatarBone.KneeLeft, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.HipRight, AvatarBone.KneeRight, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.KneeLeft, AvatarBone.AnkleLeft, jointErrorReduction, jointSoftness); AddJoint(ragdoll, skeleton, AvatarBone.KneeRight, AvatarBone.AnkleRight, jointErrorReduction, jointSoftness); // ----- Add constraint limits. // We use TwistSwingLimits to define an allowed twist and swing cone for the joints. // Exceptions are the back and knees, where we use AngularLimits to create hinges. // (We could also create a hinge with a TwistSwingLimit where the twist axis is the hinge // axis and no swing is allowed - but AngularLimits create more stable hinges.) // Another exception are the collar bones joint. We use AngularLimits to disallow any // rotations. AddAngularLimit(ragdoll, skeleton, AvatarBone.Root, AvatarBone.BackLower, skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.BackLower).Rotation.Conjugated.ToRotationMatrix33(), skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.BackLower).Rotation.Conjugated.ToRotationMatrix33(), new Vector3F(-0.3f, 0, 0), new Vector3F(0.3f, 0, 0)); AddAngularLimit(ragdoll, skeleton, AvatarBone.BackLower, AvatarBone.BackUpper, skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.BackUpper).Rotation.Conjugated.ToRotationMatrix33(), skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.BackUpper).Rotation.Conjugated.ToRotationMatrix33(), new Vector3F(-0.3f, 0, 0), new Vector3F(0.4f, 0, 0)); var rotationZ90Degrees = Matrix33F.CreateRotationZ(ConstantsF.PiOver2); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.BackUpper, AvatarBone.Neck, rotationZ90Degrees, rotationZ90Degrees, new Vector3F(-0.1f, -0.3f, -0.3f), new Vector3F(+0.1f, +0.3f, +0.3f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.Neck, AvatarBone.Head, rotationZ90Degrees, rotationZ90Degrees, new Vector3F(-0.1f, -0.6f, -0.6f), new Vector3F(+0.1f, +0.6f, +0.6f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.BackUpper, AvatarBone.CollarLeft, skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.CollarLeft).Rotation.Conjugated.ToRotationMatrix33(), skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.CollarLeft).Rotation.Conjugated.ToRotationMatrix33(), new Vector3F(0), new Vector3F(0)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.BackUpper, AvatarBone.CollarRight, skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.CollarRight).Rotation.Conjugated.ToRotationMatrix33(), skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.CollarRight).Rotation.Conjugated.ToRotationMatrix33(), new Vector3F(0), new Vector3F(0)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.CollarLeft, AvatarBone.ShoulderLeft, Matrix33F.Identity, Matrix33F.CreateRotationY(0.7f), new Vector3F(-0.7f, -1.2f, -1.2f), new Vector3F(+0.7f, +1.2f, +1.2f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.CollarRight, AvatarBone.ShoulderRight, Matrix33F.Identity, Matrix33F.CreateRotationY(-0.7f), new Vector3F(-0.7f, -1.2f, -1.2f), new Vector3F(+0.7f, +1.2f, +1.2f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.ShoulderLeft, AvatarBone.ElbowLeft, Matrix33F.Identity, Matrix33F.CreateRotationY(1.2f), new Vector3F(-0.7f, -1.2f, -1.2f), new Vector3F(+0.7f, +1.2f, +1.2f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.ShoulderRight, AvatarBone.ElbowRight, Matrix33F.Identity, Matrix33F.CreateRotationY(-1.2f), new Vector3F(-0.7f, -1.2f, -1.2f), new Vector3F(+0.7f, +1.2f, +1.2f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.ElbowLeft, AvatarBone.WristLeft, Matrix33F.Identity, Matrix33F.Identity, new Vector3F(-0.7f, -0.7f, -0.7f), new Vector3F(+0.7f, +0.7f, +0.7f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.ElbowRight, AvatarBone.WristRight, Matrix33F.Identity, Matrix33F.Identity, new Vector3F(-0.7f, -0.7f, -0.7f), new Vector3F(+0.7f, +0.7f, +0.7f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.Root, AvatarBone.HipLeft, rotationZ90Degrees, Matrix33F.CreateRotationX(-1.2f) * Matrix33F.CreateRotationZ(ConstantsF.PiOver2 + 0.2f), new Vector3F(-0.1f, -1.5f, -0.7f), new Vector3F(+0.1f, +1.5f, +0.7f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.Root, AvatarBone.HipRight, rotationZ90Degrees, Matrix33F.CreateRotationX(-1.2f) * Matrix33F.CreateRotationZ(ConstantsF.PiOver2 - 0.2f), new Vector3F(-0.1f, -1.5f, -0.7f), new Vector3F(+0.1f, +1.5f, +0.7f)); AddAngularLimit(ragdoll, skeleton, AvatarBone.HipLeft, AvatarBone.KneeLeft, skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.KneeLeft).Rotation.Conjugated.ToRotationMatrix33(), skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.KneeLeft).Rotation.Conjugated.ToRotationMatrix33(), new Vector3F(0, 0, 0), new Vector3F(2.2f, 0, 0)); AddAngularLimit(ragdoll, skeleton, AvatarBone.HipRight, AvatarBone.KneeRight, skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.KneeRight).Rotation.Conjugated.ToRotationMatrix33(), skeleton.GetBindPoseAbsoluteInverse((int)AvatarBone.KneeRight).Rotation.Conjugated.ToRotationMatrix33(), new Vector3F(0, 0, 0), new Vector3F(2.2f, 0, 0)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.KneeLeft, AvatarBone.AnkleLeft, rotationZ90Degrees, rotationZ90Degrees, new Vector3F(-0.1f, -0.7f, -0.3f), new Vector3F(+0.1f, +0.7f, +0.3f)); AddTwistSwingLimit(ragdoll, skeleton, AvatarBone.KneeRight, AvatarBone.AnkleRight, rotationZ90Degrees, rotationZ90Degrees, new Vector3F(-0.1f, -0.7f, -0.3f), new Vector3F(+0.1f, +0.7f, +0.3f)); // ----- Add motors // We use QuaternionMotors to create forces that rotate the bones into desired poses. // This can be used for damping, spring or animating the ragdoll. AddMotor(ragdoll, (AvatarBone)(-1), AvatarBone.Root); AddMotor(ragdoll, AvatarBone.Root, AvatarBone.BackLower); AddMotor(ragdoll, AvatarBone.BackLower, AvatarBone.BackUpper); AddMotor(ragdoll, AvatarBone.BackUpper, AvatarBone.Neck); AddMotor(ragdoll, AvatarBone.Neck, AvatarBone.Head); AddMotor(ragdoll, AvatarBone.BackUpper, AvatarBone.CollarLeft); AddMotor(ragdoll, AvatarBone.BackUpper, AvatarBone.CollarRight); AddMotor(ragdoll, AvatarBone.CollarLeft, AvatarBone.ShoulderLeft); AddMotor(ragdoll, AvatarBone.CollarRight, AvatarBone.ShoulderRight); AddMotor(ragdoll, AvatarBone.ShoulderLeft, AvatarBone.ElbowLeft); AddMotor(ragdoll, AvatarBone.ShoulderRight, AvatarBone.ElbowRight); AddMotor(ragdoll, AvatarBone.ElbowLeft, AvatarBone.WristLeft); AddMotor(ragdoll, AvatarBone.ElbowRight, AvatarBone.WristRight); AddMotor(ragdoll, AvatarBone.Root, AvatarBone.HipLeft); AddMotor(ragdoll, AvatarBone.Root, AvatarBone.HipRight); AddMotor(ragdoll, AvatarBone.HipLeft, AvatarBone.KneeLeft); AddMotor(ragdoll, AvatarBone.HipRight, AvatarBone.KneeRight); AddMotor(ragdoll, AvatarBone.KneeLeft, AvatarBone.AnkleLeft); AddMotor(ragdoll, AvatarBone.KneeRight, AvatarBone.AnkleRight); // ----- Set collision filters. // Collisions between connected bones have been disabled with Constraint.CollisionEnabled // = false in the joints. We need to disable a few other collisions. // Following bodies do not collide with anything. They are only used to connect other // bones. ragdoll.Bodies[(int)AvatarBone.Neck].CollisionObject.Enabled = false; ragdoll.Bodies[(int)AvatarBone.CollarLeft].CollisionObject.Enabled = false; ragdoll.Bodies[(int)AvatarBone.CollarRight].CollisionObject.Enabled = false; // We disable filters for following body pairs because they are usually penetrating each // other, which needs to be ignored. var filter = simulation.CollisionDomain.CollisionDetection.CollisionFilter as CollisionFilter; if (filter != null) { filter.Set(ragdoll.Bodies[(int)AvatarBone.BackUpper].CollisionObject, ragdoll.Bodies[(int)AvatarBone.ShoulderLeft].CollisionObject, false); filter.Set(ragdoll.Bodies[(int)AvatarBone.BackUpper].CollisionObject, ragdoll.Bodies[(int)AvatarBone.ShoulderRight].CollisionObject, false); } return(ragdoll); }
public static void Load(CollisionDomain collisionDomain) { // Create a box for the ground. AddObject("Ground", new Pose(new Vector3F(0, -5, 0)), new BoxShape(60, 10, 60), collisionDomain); // Create a small flying sphere to visualize the approx. head height. - This is just // for debugging so that we have a feeling for heights. AddObject("Sphere", new Pose(new Vector3F(0, 1.5f, 0)), new SphereShape(0.2f), collisionDomain); // Create small walls at the level boundary. AddObject("WallLeft", new Pose(new Vector3F(-30, 1, 0)), new BoxShape(0.3f, 2, 60), collisionDomain); AddObject("WallRight", new Pose(new Vector3F(30, 1, 0)), new BoxShape(0.3f, 2, 60), collisionDomain); AddObject("WallFront", new Pose(new Vector3F(0, 1, -30)), new BoxShape(60, 2, 0.3f), collisionDomain); AddObject("WallBack", new Pose(new Vector3F(0, 1, 30)), new BoxShape(60, 2, 0.3f), collisionDomain); // Create a few bigger objects. // We position the boxes so that we have a few corners we can run into. Character controllers // should be stable when the user runs into corners. AddObject("House0", new Pose(new Vector3F(10, 1, -10)), new BoxShape(8, 2, 8f), collisionDomain); AddObject("House1", new Pose(new Vector3F(13, 1, -4)), new BoxShape(2, 2, 4), collisionDomain); AddObject("House2", new Pose(new Vector3F(10, 2, -15), Matrix33F.CreateRotationY(-0.3f)), new BoxShape(8, 4, 2), collisionDomain); // // Create stairs with increasing step height. // // Each step is a box. With this object we can test if our character can climb up // stairs. The character controller has a step height limit. Increasing step heights // let us test if the step height limit works. float startHeight = 0; const float stepDepth = 1f; for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.05f; Pose pose = new Pose(new Vector3F(0, startHeight + stepHeight / 2, -2 - i * stepDepth)); BoxShape shape = new BoxShape(2, stepHeight, stepDepth); AddObject("Step" + i, pose, shape, collisionDomain); startHeight += stepHeight; } // // Create a height field. // // Terrain that is uneven is best modeled with a height field. Height fields are faster // than general triangle meshes. // The height direction is the y direction. // The height field lies in the x/z plane. var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; // Create arbitrary height values. for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { if (x == 0 || z == 0 || x == 19 || z == 19) { // Set this boundary elements to a low height, so that the height field is connected // to the ground. samples[z * numberOfSamplesX + x] = -1; } else { // A sine/cosine function that creates some interesting waves. samples[z * numberOfSamplesX + x] = 1.0f + (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 1.0f); } } } var heightField = new HeightField(0, 0, 20, 20, samples, numberOfSamplesX, numberOfSamplesZ); AddObject("Heightfield", new Pose(new Vector3F(10, 0, 10)), heightField, collisionDomain); // Create rubble on the floor (small random objects on the floor). // Our character should be able to move over small bumps on the ground. for (int i = 0; i < 50; i++) { Pose pose = new Pose( new Vector3F(RandomHelper.Random.NextFloat(-5, 5), 0, RandomHelper.Random.NextFloat(10, 20)), RandomHelper.Random.NextQuaternionF()); BoxShape shape = new BoxShape(RandomHelper.Random.NextVector3F(0.05f, 0.8f)); AddObject("Stone" + i, pose, shape, collisionDomain); } // Create some slopes to see how our character performs on/under sloped surfaces. // Here we can test how the character controller behaves if the head touches an inclined // ceiling. AddObject("SlopeGround", new Pose(new Vector3F(-2, 1.8f, -12), QuaternionF.CreateRotationX(0.4f)), new BoxShape(2, 0.5f, 10), collisionDomain); AddObject("SlopeRoof", new Pose(new Vector3F(-2, 5.6f, -12), QuaternionF.CreateRotationX(-0.4f)), new BoxShape(2, 0.5f, 10), collisionDomain); // Slopes with different tilt angles. // The character controller has a slope limit. Only flat slopes should be climbable. for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.1f; Pose pose = new Pose( new Vector3F(-10, i * 0.5f, -i * 2), Matrix33F.CreateRotationX(MathHelper.ToRadians(10) + i * MathHelper.ToRadians(10))); BoxShape shape = new BoxShape(8 * (1 - i * 0.1f), 0.5f, 30); AddObject("Slope" + i, pose, shape, collisionDomain); startHeight += stepHeight; } // Create a slope with a wall on one side. // This objects let's us test how the character controller behaves while falling and // sliding along a vertical wall. (Run up the slope and then jump down while moving into // the wall.) AddObject("LongSlope", new Pose(new Vector3F(-20, 3, -10), Matrix33F.CreateRotationX(0.4f)), new BoxShape(4, 5f, 30), collisionDomain); AddObject("LongSlopeWall", new Pose(new Vector3F(-22, 5, -10)), new BoxShape(0.5f, 10f, 25), collisionDomain); // Create a mesh object to test walking on triangle meshes. // Normally, the mesh would be loaded from a file. Here, we make a composite shape and // let DigitalRune Geometry compute a mesh for it. Then we throw away the composite // shape and use only the mesh. - We do this to test triangle meshes. Using the composite // shape instead of the triangle mesh would be a lot faster. CompositeShape compositeShape = new CompositeShape(); compositeShape.Children.Add(new GeometricObject(heightField, Pose.Identity)); compositeShape.Children.Add(new GeometricObject(new CylinderShape(1, 2), new Pose(new Vector3F(10, 1, 10)))); compositeShape.Children.Add(new GeometricObject(new SphereShape(3), new Pose(new Vector3F(15, 0, 15)))); compositeShape.Children.Add(new GeometricObject(new BoxShape(1, 2, 3), new Pose(new Vector3F(15, 0, 5)))); ITriangleMesh mesh = compositeShape.GetMesh(0.01f, 3); TriangleMeshShape meshShape = new TriangleMeshShape(mesh); // Collision detection speed for triangle meshes can be improved by using a spatial // partition. Here, we assign an AabbTree to the triangle mesh shape. The tree is // built automatically when needed and it stores triangle indices (therefore the generic // parameter of the AabbTree is int). meshShape.Partition = new AabbTree <int>() { // The tree is automatically built using a mixed top-down/bottom-up approach. Bottom-up // building is slower but produces better trees. If the tree building takes too long, // we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; AddObject("Mesh", new Pose(new Vector3F(-30, 0, 10)), meshShape, collisionDomain); }
public void SetUp() { child0 = new GeometricObject(new CircleShape(3), new Pose(new Vector3F(), QuaternionF.CreateRotationX(ConstantsF.PiOver2))); child1 = new GeometricObject(new LineSegmentShape(new Vector3F(0, 5, 0), new Vector3F(0, -5, 0)), Pose.Identity); cs = new MinkowskiSumShape { ObjectA = child0, ObjectB = child1 }; }
public void Constructor() { Assert.AreEqual(Vector3F.Zero, ((PointShape) new MinkowskiDifferenceShape().ObjectA.Shape).Position); Assert.AreEqual(Vector3F.Zero, ((PointShape) new MinkowskiDifferenceShape().ObjectB.Shape).Position); Assert.AreEqual(Pose.Identity, new MinkowskiDifferenceShape().ObjectA.Pose); Assert.AreEqual(Pose.Identity, new MinkowskiDifferenceShape().ObjectB.Pose); var m = new MinkowskiDifferenceShape( new GeometricObject(new CircleShape(3), new Pose(new Vector3F(1, 0, 0), QuaternionF.CreateRotationX(ConstantsF.PiOver2))), new GeometricObject(new LineSegmentShape(new Vector3F(0, 5, 0), new Vector3F(0, -5, 0)), Pose.Identity)); Assert.AreEqual(new Vector3F(1, 0, 0), m.ObjectA.Pose.Position); Assert.AreEqual(new Vector3F(0, 0, 0), m.ObjectB.Pose.Position); }
public void SetUp() { cs = new MinkowskiDifferenceShape(); cs.ObjectA = new GeometricObject(new CircleShape(3), new Pose(new Vector3F(1, 0, 0), QuaternionF.CreateRotationX(ConstantsF.PiOver2))); cs.ObjectB = new GeometricObject(new LineSegmentShape(new Vector3F(0, 5, 0), new Vector3F(0, -5, 0)), Pose.Identity); }
private bool _drawDebugInfo; // true if the collision shapes should be drawn for debugging. public ContentPipelineSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3F(0, 1, 10), 0, 0); // Initialize collision detection. // Note: The physics Simulation also has a collision domain (Simulation.CollisionDomain) // which we could use and which is updated together with the Simulation in // SampleGame.cs. But in this example we create our own CollisionDomain for demonstration // purposes. _collisionDomain = new CollisionDomain(new CollisionDetection()); // Register CollisionDomain in service container. Services.Register(typeof(CollisionDomain), null, _collisionDomain); // Add game objects which manage graphics models and their collision representations. _saucerObject = new SaucerObject(Services) { Name = "Saucer" }; _shipObjectA = new ShipObject(Services) { Name = "ShipA" }; _shipObjectB = new ShipObject(Services) { Name = "ShipB" }; GameObjectService.Objects.Add(_saucerObject); GameObjectService.Objects.Add(_shipObjectA); GameObjectService.Objects.Add(_shipObjectB); // Position the second ship right of the first ship with an arbitrary rotation. _shipObjectB.Pose = new Pose(new Vector3F(2, 0, 0), QuaternionF.CreateRotationY(0.7f) * QuaternionF.CreateRotationX(1.2f)); // Position the saucer left of the first ship with an arbitrary rotation. _saucerObject.Pose = new Pose(new Vector3F(-2.5f, 0, 0), QuaternionF.CreateRotationY(0.2f) * QuaternionF.CreateRotationX(0.4f)); }
// Handle character-related input and move the character. private void ControlCharacter(float deltaTime) { // Compute new orientation from mouse movement. float deltaYaw = -InputService.MousePositionDelta.X; _yaw += deltaYaw * deltaTime * 0.1f; float deltaPitch = -InputService.MousePositionDelta.Y; _pitch += deltaPitch * deltaTime * 0.1f; // Limit the pitch angle. _pitch = MathHelper.Clamp(_pitch, -ConstantsF.PiOver2, ConstantsF.PiOver2); // Compute new orientation of the camera. QuaternionF cameraOrientation = QuaternionF.CreateRotationY(_yaw) * QuaternionF.CreateRotationX(_pitch); // Create velocity from WASD keys. // TODO: Diagonal movement is faster ;-). Fix this. Vector3F velocityVector = Vector3F.Zero; if (Keyboard.GetState().IsKeyDown(Keys.W)) { velocityVector.Z--; } if (Keyboard.GetState().IsKeyDown(Keys.S)) { velocityVector.Z++; } if (Keyboard.GetState().IsKeyDown(Keys.A)) { velocityVector.X--; } if (Keyboard.GetState().IsKeyDown(Keys.D)) { velocityVector.X++; } velocityVector *= 10 * deltaTime; // Velocity vector is currently in view space. -z is the forward direction. // We have to convert this vector to world space by rotating it. velocityVector = QuaternionF.CreateRotationY(_yaw).Rotate(velocityVector); // New compute desired character controller position in world space: Vector3F targetPosition = _character.Position + velocityVector; // Check if user wants to jump. bool jump = Keyboard.GetState().IsKeyDown(Keys.Space); // Call character controller to compute a new valid position. The character // controller slides along obstacles, handles stepping up/down, etc. _character.Move(targetPosition, deltaTime, jump); // ----- Set view matrix for graphics. // For third person we move the eye position back, behind the body (+z direction is // the "back" direction). Vector3F thirdPersonDistance = cameraOrientation.Rotate(new Vector3F(0, 0, 6)); // Compute camera pose (= position + orientation). _cameraNode.PoseWorld = new Pose { Position = _character.Position // Floor position of character + new Vector3F(0, 1.6f, 0) // + Eye height + thirdPersonDistance, Orientation = cameraOrientation.ToRotationMatrix33() }; }
// OnUpdate() is called once per frame. protected override void OnUpdate(TimeSpan deltaTime) { // Mouse centering (controlled by the MenuComponent) is disabled if the game // is inactive or if the GUI is active. In these cases, we do not want to move // the player. if (!_inputService.EnableMouseCentering) { return; } if (!IsEnabled) { return; } float deltaTimeF = (float)deltaTime.TotalSeconds; // Compute new orientation from mouse movement, gamepad and touch. Vector2F mousePositionDelta = _inputService.MousePositionDelta; GamePadState gamePadState = _inputService.GetGamePadState(LogicalPlayerIndex.One); Vector2F touchDelta = Vector2F.Zero; #if MONOGAME || WINDOWS_PHONE foreach (var gesture in _inputService.Gestures) { if (gesture.GestureType == GestureType.FreeDrag) { touchDelta += (Vector2F)gesture.Delta; // If we have touch input, we ignore the mouse movement mousePositionDelta = Vector2F.Zero; } } #endif #if WINDOWS_PHONE || IOS // On Windows Phone touch input also sets the mouse input. --> Ignore mouse data. mousePositionDelta = Vector2F.Zero; #endif float deltaYaw = -mousePositionDelta.X - touchDelta.X - gamePadState.ThumbSticks.Right.X * ThumbStickFactor; _currentYaw += deltaYaw * deltaTimeF * AngularVelocityMagnitude; float deltaPitch = -mousePositionDelta.Y - touchDelta.Y + gamePadState.ThumbSticks.Right.Y * ThumbStickFactor; _currentPitch += deltaPitch * deltaTimeF * AngularVelocityMagnitude; // Limit the pitch angle to +/- 90°. _currentPitch = MathHelper.Clamp(_currentPitch, -ConstantsF.PiOver2, ConstantsF.PiOver2); // Reset camera position if <Home> or <Right Stick> is pressed. if (_inputService.IsPressed(Keys.Home, false) || _inputService.IsPressed(Buttons.RightStick, false, LogicalPlayerIndex.One)) { ResetPose(); } // Compute new orientation of the camera. QuaternionF orientation = QuaternionF.CreateRotationY(_currentYaw) * QuaternionF.CreateRotationX(_currentPitch); // Create velocity from <W>, <A>, <S>, <D> and <R>, <F> keys. // <R> or DPad up is used to move up ("rise"). // <F> or DPad down is used to move down ("fall"). Vector3F velocity = Vector3F.Zero; KeyboardState keyboardState = _inputService.KeyboardState; if (keyboardState.IsKeyDown(Keys.W)) { velocity.Z--; } if (keyboardState.IsKeyDown(Keys.S)) { velocity.Z++; } if (keyboardState.IsKeyDown(Keys.A)) { velocity.X--; } if (keyboardState.IsKeyDown(Keys.D)) { velocity.X++; } if (keyboardState.IsKeyDown(Keys.R) || gamePadState.DPad.Up == ButtonState.Pressed) { velocity.Y++; } if (keyboardState.IsKeyDown(Keys.F) || gamePadState.DPad.Down == ButtonState.Pressed) { velocity.Y--; } // Add velocity from gamepad sticks. velocity.X += gamePadState.ThumbSticks.Left.X; velocity.Z -= gamePadState.ThumbSticks.Left.Y; // Rotate the velocity vector from view space to world space. velocity = orientation.Rotate(velocity); if (keyboardState.IsKeyDown(Keys.LeftShift)) { velocity *= SpeedBoost; } // Multiply the velocity by time to get the translation for this frame. Vector3F translation = velocity * LinearVelocityMagnitude * deltaTimeF; // Update SceneNode.LastPoseWorld - this is required for some effects, like // camera motion blur. CameraNode.LastPoseWorld = CameraNode.PoseWorld; // Set the new camera pose. CameraNode.PoseWorld = new Pose( CameraNode.PoseWorld.Position + translation, orientation); }
public void SetUp() { child0 = new GeometricObject(new CircleShape(3), new Pose(new Vector3F(0, 5, 0), QuaternionF.CreateRotationX(ConstantsF.PiOver2))); child1 = new GeometricObject(new CircleShape(3), new Pose(new Vector3F(0, -5, 0), QuaternionF.CreateRotationX(ConstantsF.PiOver2))); cs = new ConvexHullOfShapes(); cs.Children.Add(child0); cs.Children.Add(child1); }
//-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="Spotlight"/> class. /// </summary> public Spotlight() { Color = Vector3F.One; DiffuseIntensity = 1; SpecularIntensity = 1; HdrScale = 1; _falloffAngle = 20.0f * ConstantsF.Pi / 180; Shape = new TransformedShape(new GeometricObject(new ConeShape((float)Math.Tan(MathHelper.ToRadians(30)) * 5, 5), new Pose(new Vector3F(0, 0, -5), QuaternionF.CreateRotationX(ConstantsF.PiOver2)))); Attenuation = 2; }
protected override void OnUpdate(TimeSpan deltaTime) { // Mouse centering (controlled by the MenuComponent) is disabled if the game // is inactive or if the GUI is active. In these cases, we do not want to move // the player. if (!_inputService.EnableMouseCentering) { return; } float deltaTimeF = (float)deltaTime.TotalSeconds; // ----- Hulk Mode // Toggle "Hulk" mode if <H> or <X> (gamepad) is pressed. if (_inputService.IsPressed(Keys.H, false) || _inputService.IsPressed(Buttons.X, false, LogicalPlayerIndex.One)) { ToggleHulk(); } // ----- Crouching if (_inputService.IsPressed(Keys.LeftShift, false) || _inputService.IsPressed(Buttons.RightTrigger, false, LogicalPlayerIndex.One)) { Crouch(); } else if (!_inputService.IsDown(Keys.LeftShift) && !_inputService.IsDown(Buttons.RightTrigger, LogicalPlayerIndex.One) && CharacterController.Height <= 1) { StandUp(); } // ----- Update orientation // Update _yaw and _pitch. UpdateOrientation(deltaTimeF); // Compute the new orientation of the camera. QuaternionF orientation = QuaternionF.CreateRotationY(_yaw) * QuaternionF.CreateRotationX(_pitch); // ----- Compute translation // Create velocity from <W>, <A>, <S>, <D> and gamepad sticks. Vector3F moveDirection = Vector3F.Zero; if (Keyboard.GetState().IsKeyDown(Keys.W)) { moveDirection.Z--; } if (Keyboard.GetState().IsKeyDown(Keys.S)) { moveDirection.Z++; } if (Keyboard.GetState().IsKeyDown(Keys.A)) { moveDirection.X--; } if (Keyboard.GetState().IsKeyDown(Keys.D)) { moveDirection.X++; } GamePadState gamePadState = _inputService.GetGamePadState(LogicalPlayerIndex.One); moveDirection.X += gamePadState.ThumbSticks.Left.X; moveDirection.Z -= gamePadState.ThumbSticks.Left.Y; // Rotate the velocity vector from view space to world space. moveDirection = orientation.Rotate(moveDirection); // Add velocity from <R>, <F> keys. // <R> or DPad up is used to move up ("rise"). // <F> or DPad down is used to move down ("fall"). if (Keyboard.GetState().IsKeyDown(Keys.R) || gamePadState.DPad.Up == ButtonState.Pressed) { moveDirection.Y++; } if (Keyboard.GetState().IsKeyDown(Keys.F) || gamePadState.DPad.Down == ButtonState.Pressed) { moveDirection.Y--; } // ----- Climbing bool hasLadderContact = HasLadderContact(); bool hasLedgeContact = HasLedgeContact(); CharacterController.IsClimbing = hasLadderContact || hasLedgeContact; // When the character is walking (gravity > 0) it cannot walk up/down - only on a ladder. if (CharacterController.Gravity != 0 && !hasLadderContact) { moveDirection.Y = 0; } // ----- Moving moveDirection.TryNormalize(); Vector3F moveVelocity = moveDirection * LinearVelocityMagnitude; // ----- Jumping if ((_inputService.IsPressed(Keys.Space, false) || _inputService.IsPressed(Buttons.A, false, LogicalPlayerIndex.One)) && (CharacterController.HasGroundContact || CharacterController.IsClimbing)) { // Jump button was newly pressed and the character has support to start the jump. _timeSinceLastJump = 0; } float jumpVelocity = 0; if ((_inputService.IsDown(Keys.Space) || _inputService.IsDown(Buttons.A, LogicalPlayerIndex.One))) { // Jump button is still down. if (_timeSinceLastJump + deltaTimeF <= DynamicJumpTime) { // The DynamicJumpTime has not been exceeded. // Set a jump velocity to make the jump higher. jumpVelocity = JumpVelocity; } else if (_timeSinceLastJump <= DynamicJumpTime) { // The jump time exceeds DynamicJumpTime in this time step. // _timeSinceLastJump <= DynamicJumpTime // _timeSinceLastJump + deltaTime > DynamicJumpTime // In order to achieve exact, reproducible jump heights we need to split // the time step: float deltaTime0 = DynamicJumpTime - _timeSinceLastJump; float deltaTime1 = deltaTimeF - deltaTime0; // The first part of the movement is a jump with active jump velocity. jumpVelocity = JumpVelocity; _timeSinceLastJump += deltaTime0; CharacterController.Move(moveVelocity, jumpVelocity, deltaTime0); // The second part of the movement is a jump without jump velocity. jumpVelocity = 0; deltaTimeF = deltaTime1; } } _timeSinceLastJump += deltaTimeF; // ----- Move the character. CharacterController.Move(moveVelocity, jumpVelocity, deltaTimeF); // Draw character controller capsule. // The character controller is also transparent The hulk is green, of course. var color = _isHulk ? Color.DarkGreen : Color.Gray; color.A = 128; _debugRenderer.DrawObject(CharacterController.Body, color, false, false); }
// OnUpdate() is called once per frame. protected override void OnUpdate(TimeSpan deltaTime) { #if !WINDOWS_PHONE // Mouse centering (controlled by the MenuComponent) is disabled if the game // is inactive or if the GUI is active. In these cases, we do not want to move // the player. if (!_inputService.EnableMouseCentering) { return; } #endif if (!IsEnabled) { return; } float deltaTimeF = (float)deltaTime.TotalSeconds; // Compute new orientation from mouse movement and gamepad. Vector2F mousePositionDelta = _inputService.MousePositionDelta; #if !WINDOWS_PHONE GamePadState gamePadState = _inputService.GetGamePadState(LogicalPlayerIndex.One); float deltaYaw = -mousePositionDelta.X - gamePadState.ThumbSticks.Right.X * ThumbStickFactor; _currentYaw += deltaYaw * deltaTimeF * AngularVelocityMagnitude; float deltaPitch = -mousePositionDelta.Y + gamePadState.ThumbSticks.Right.Y * ThumbStickFactor; _currentPitch += deltaPitch * deltaTimeF * AngularVelocityMagnitude; // Limit the pitch angle to +/- 90°. _currentPitch = MathHelper.Clamp(_currentPitch, -ConstantsF.PiOver2, ConstantsF.PiOver2); // Reset camera position if <Home> or <Right Stick> is pressed. if (_inputService.IsPressed(Keys.Home, false) || _inputService.IsPressed(Buttons.RightStick, false, LogicalPlayerIndex.One)) { ResetPose(); } // Compute new orientation of the camera. QuaternionF orientation = QuaternionF.CreateRotationY(_currentYaw) * QuaternionF.CreateRotationX(_currentPitch); // Create velocity from <W>, <A>, <S>, <D> and <R>, <F> keys. // <R> or DPad up is used to move up ("rise"). // <F> or DPad down is used to move down ("fall"). Vector3F velocity = Vector3F.Zero; KeyboardState keyboardState = _inputService.KeyboardState; if (keyboardState.IsKeyDown(Keys.W)) { velocity.Z--; } if (keyboardState.IsKeyDown(Keys.S)) { velocity.Z++; } if (keyboardState.IsKeyDown(Keys.A)) { velocity.X--; } if (keyboardState.IsKeyDown(Keys.D)) { velocity.X++; } if (keyboardState.IsKeyDown(Keys.R) || gamePadState.DPad.Up == ButtonState.Pressed) { velocity.Y++; } if (keyboardState.IsKeyDown(Keys.F) || gamePadState.DPad.Down == ButtonState.Pressed) { velocity.Y--; } // Add velocity from gamepad sticks. velocity.X += gamePadState.ThumbSticks.Left.X; velocity.Z -= gamePadState.ThumbSticks.Left.Y; // Rotate the velocity vector from view space to world space. velocity = orientation.Rotate(velocity); if (keyboardState.IsKeyDown(Keys.LeftShift)) { velocity *= SpeedBoost; } // Multiply the velocity by time to get the translation for this frame. Vector3F translation = velocity * LinearVelocityMagnitude * deltaTimeF; // Update SceneNode.LastPoseWorld - this is required for some effects, like // camera motion blur. CameraNode.LastPoseWorld = CameraNode.PoseWorld; // Set the new camera pose. CameraNode.PoseWorld = new Pose( CameraNode.PoseWorld.Position + translation, orientation); #else // Only handle mouse movement is mouse button was down the last frame. if (!_inputService.IsPressed(MouseButtons.Left, false)) { float deltaYaw = -mousePositionDelta.X; _currentYaw += deltaYaw * deltaTimeF * AngularVelocityMagnitude; float deltaPitch = -mousePositionDelta.Y; _currentPitch += deltaPitch * deltaTimeF * AngularVelocityMagnitude; // Limit the pitch angle. _currentPitch = MathHelper.Clamp(_currentPitch, -1, 1); // Compute new orientation of the camera. QuaternionF orientation = QuaternionF.CreateRotationY(_currentYaw) * QuaternionF.CreateRotationX(_currentPitch); var radius = CameraNode.PoseWorld.Position.Length; // Update SceneNode.LastPoseWorld - this is required for some effects, like // camera motion blur. CameraNode.LastPoseWorld = CameraNode.PoseWorld; // Set the new camera pose. CameraNode.PoseWorld = new Pose(orientation.Rotate(new Vector3F(0, 0, radius)), orientation); } #endif }