예제 #1
0
        public void ToLocal()
        {
            Vector3F point0 = new Vector3F(1, 0.5f, 0.5f);
            Vector3F point1 = new Vector3F(0.5f, 1, 0.5f);
            Vector3F point2 = new Vector3F(0.5f, 0.5f, 1);
            Plane    plane  = new Plane(point0, point1, point2);

            Vector3F pointAbove = plane.Normal * plane.DistanceFromOrigin * 2;
            Vector3F pointBelow = plane.Normal * plane.DistanceFromOrigin * 0.5f;

            Assert.IsTrue(Vector3F.Dot(plane.Normal, pointAbove) > plane.DistanceFromOrigin);
            Assert.IsTrue(Vector3F.Dot(plane.Normal, pointBelow) < plane.DistanceFromOrigin);

            Pose pose = new Pose(new Vector3F(-5, 100, -20), Matrix33F.CreateRotation(new Vector3F(1, 2, 3), 0.123f));

            point0     = pose.ToLocalPosition(point0);
            point1     = pose.ToLocalPosition(point1);
            point2     = pose.ToLocalPosition(point2);
            pointAbove = pose.ToLocalPosition(pointAbove);
            pointBelow = pose.ToLocalPosition(pointBelow);
            plane.ToLocal(ref pose);

            Assert.IsTrue(plane.Normal.IsNumericallyNormalized);

            Vector3F dummy;

            Assert.IsTrue(GeometryHelper.GetClosestPoint(plane, point0, out dummy));
            Assert.IsTrue(GeometryHelper.GetClosestPoint(plane, point1, out dummy));
            Assert.IsTrue(GeometryHelper.GetClosestPoint(plane, point2, out dummy));
            Assert.IsTrue(Vector3F.Dot(plane.Normal, pointAbove) > plane.DistanceFromOrigin);
            Assert.IsTrue(Vector3F.Dot(plane.Normal, pointBelow) < plane.DistanceFromOrigin);
        }
예제 #2
0
 public void IsRotation()
 {
     Assert.IsTrue(!Matrix33F.Zero.IsRotation);
     Assert.IsTrue(Matrix33F.Identity.IsRotation);
     Assert.IsTrue(Matrix33F.CreateRotation(new Vector3F(1, 2, 3).Normalized, 0.5f).IsRotation);
     Assert.IsTrue(!new Matrix33F(1, 0, 0, 0, 1, 0, 0, 0, -1).IsRotation);
 }
예제 #3
0
        public void GetScreenSizeWithPerspective()
        {
            // Camera
            var projection = new PerspectiveProjection();

            projection.SetFieldOfView(MathHelper.ToRadians(90), 2.0f / 1.0f, 1.0f, 100f);
            var camera     = new Camera(projection);
            var cameraNode = new CameraNode(camera);

            cameraNode.PoseWorld = new Pose(new Vector3F(123, 456, -789), Matrix33F.CreateRotation(new Vector3F(1, -2, 3), MathHelper.ToRadians(75)));

            // 2:1 viewport
            var viewport = new Viewport(10, 10, 200, 100);

            // Test object
            var shape           = new SphereShape();
            var geometricObject = new GeometricObject(shape);

            // Empty sphere at camera position.
            shape.Radius         = 0;
            geometricObject.Pose = cameraNode.PoseWorld;
            Vector2F screenSize = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject);

            Assert.AreEqual(0, screenSize.X);
            Assert.AreEqual(0, screenSize.Y);

            // Empty sphere centered at near plane.
            shape.Radius         = 0;
            geometricObject.Pose = cameraNode.PoseWorld * new Pose(new Vector3F(0.123f, -0.543f, -1));
            screenSize           = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject);
            Assert.AreEqual(0, screenSize.X);
            Assert.AreEqual(0, screenSize.Y);

            // Create sphere which as a bounding sphere of ~1 unit diameter:
            // Since the bounding sphere is based on the AABB, we need to make the
            // actual sphere a bit smaller.
            shape.Radius = 1 / (2 * (float)Math.Sqrt(3)) + Numeric.EpsilonF;

            // Sphere at camera position.
            geometricObject.Pose = cameraNode.PoseWorld;
            screenSize           = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject);
            Assert.Greater(screenSize.X, 200);
            Assert.Greater(screenSize.Y, 100);

            // Sphere at near plane.
            geometricObject.Pose = cameraNode.PoseWorld * new Pose(new Vector3F(0.123f, -0.543f, -1));
            screenSize           = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject);
            Assert.IsTrue(Numeric.AreEqual(screenSize.X, 50.0f, 10f));
            Assert.IsTrue(Numeric.AreEqual(screenSize.Y, 50.0f, 10f));

            // Double distance --> half size
            geometricObject.Pose = cameraNode.PoseWorld * new Pose(new Vector3F(0.123f, -0.543f, -2));
            screenSize           = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject);
            Assert.IsTrue(Numeric.AreEqual(screenSize.X, 25.0f, 5f));
            Assert.IsTrue(Numeric.AreEqual(screenSize.Y, 25.0f, 5f));
        }
예제 #4
0
        public void RotationMatrix33()
        {
            float       angle   = -1.6f;
            Vector3F    axis    = new Vector3F(1.0f, 2.0f, -3.0f);
            QuaternionF q       = QuaternionF.CreateRotation(axis, angle);
            Matrix33F   m33     = Matrix33F.CreateRotation(axis, angle);
            Vector3F    v       = new Vector3F(0.3f, -2.4f, 5.6f);
            Vector3F    result1 = q.ToRotationMatrix33() * v;
            Vector3F    result2 = m33 * v;

            Assert.IsTrue(Vector3F.AreNumericallyEqual(result1, result2));
        }
예제 #5
0
        public void CreateRotationZ()
        {
            float     angle = (float)MathHelper.ToRadians(30);
            Matrix33F m     = Matrix33F.CreateRotationZ(angle);

            Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F((float)Math.Cos(angle), (float)Math.Sin(angle), 0), m * Vector3F.UnitX));

            QuaternionF q = QuaternionF.CreateRotation(Vector3F.UnitZ, angle);

            Assert.IsTrue(Vector3F.AreNumericallyEqual(q.Rotate(Vector3F.One), m * Vector3F.One));

            Assert.IsTrue(Matrix33F.AreNumericallyEqual(Matrix33F.CreateRotation(Vector3F.UnitZ, angle), m));
        }
예제 #6
0
        public ConvexHullSample(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            SampleFramework.IsMouseVisible = false;
            GraphicsScreen.ClearBackground = true;
            GraphicsScreen.BackgroundColor = Color.CornflowerBlue;
            SetCamera(new Vector3F(0, 1, 10), 0, 0);

            // Generate random points.
            var points = new List <Vector3F>();

            for (int i = 0; i < 100; i++)
            {
                points.Add(RandomHelper.Random.NextVector3F(-1, 1));
            }

            // Apply random transformation to points to make this sample more interesting.
            Matrix44F transform = new Matrix44F(
                Matrix33F.CreateRotation(RandomHelper.Random.NextQuaternionF()) * Matrix33F.CreateScale(RandomHelper.Random.NextVector3F(0.1f, 2f)),
                RandomHelper.Random.NextVector3F(-1, 1));

            for (int i = 0; i < points.Count; i++)
            {
                points[i] = transform.TransformPosition(points[i]);
            }

            // Compute convex hull. The result is the mesh of the hull represented as a
            // Doubly-Connected Edge List (DCEL).
            DcelMesh convexHull = GeometryHelper.CreateConvexHull(points);

            // We don't need the DCEL representation. Let's store the hull as a simpler triangle mesh.
            TriangleMesh convexHullMesh = convexHull.ToTriangleMesh();

            // Compute a tight-fitting oriented bounding box.
            Vector3F boundingBoxExtent; // The bounding box dimensions (widths in X, Y and Z).
            Pose     boundingBoxPose;   // The pose (world space position and orientation) of the bounding box.

            GeometryHelper.ComputeBoundingBox(points, out boundingBoxExtent, out boundingBoxPose);
            // (Note: The GeometryHelper also contains methods to compute a bounding sphere.)

            var debugRenderer = GraphicsScreen.DebugRenderer;

            foreach (var point in points)
            {
                debugRenderer.DrawPoint(point, Color.White, true);
            }

            debugRenderer.DrawShape(new TriangleMeshShape(convexHullMesh), Pose.Identity, Vector3F.One, Color.Violet, false, false);
            debugRenderer.DrawBox(boundingBoxExtent.X, boundingBoxExtent.Y, boundingBoxExtent.Z, boundingBoxPose, Color.Red, true, false);
        }
예제 #7
0
        public void CreateRotation()
        {
            Matrix33F m = Matrix33F.CreateRotation(Vector3F.UnitX, 0.0f);

            Assert.AreEqual(Matrix33F.Identity, m);

            m = Matrix33F.CreateRotation(Vector3F.UnitX, (float)Math.PI / 2);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(Vector3F.UnitZ, m * Vector3F.UnitY));

            m = Matrix33F.CreateRotation(Vector3F.UnitY, (float)Math.PI / 2);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(Vector3F.UnitX, m * Vector3F.UnitZ));

            m = Matrix33F.CreateRotation(Vector3F.UnitZ, (float)Math.PI / 2);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(Vector3F.UnitY, m * Vector3F.UnitX));
        }
예제 #8
0
        public void ToLocal()
        {
            Vector3F startPoint = new Vector3F(10, 20, -40);
            Vector3F endPoint   = new Vector3F(-22, 34, 45);
            Ray      ray        = new Ray(startPoint, (endPoint - startPoint).Normalized, (endPoint - startPoint).Length);

            Pose pose = new Pose(new Vector3F(-5, 100, -20), Matrix33F.CreateRotation(new Vector3F(1, 2, 3), 0.123f));

            startPoint = pose.ToLocalPosition(startPoint);
            endPoint   = pose.ToLocalPosition(endPoint);
            ray.ToLocal(ref pose);

            Assert.IsTrue(Vector3F.AreNumericallyEqual(startPoint, ray.Origin));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(endPoint, ray.Origin + ray.Direction * ray.Length));
        }
예제 #9
0
        private void UpdateEphemeris()
        {
#if XBOX
            _ephemeris.Time = new DateTimeOffset(_time.Ticks, TimeSpan.Zero);
#else
            _ephemeris.Time = _time;
#endif
            _ephemeris.Update();

            var sunDirection  = (Vector3F)_ephemeris.SunDirectionRefracted;
            var sunUp         = sunDirection.Orthonormal1;
            var moonDirection = (Vector3F)_ephemeris.MoonPosition.Normalized;
            var moonUp        = (Vector3F)_ephemeris.EquatorialToWorld.TransformDirection(Vector3D.Up);

#if true
            _starfield.PoseWorld = new Pose((Matrix33F)_ephemeris.EquatorialToWorld.Minor);
            _sun.LookAt((Vector3F)_ephemeris.SunDirectionRefracted, sunUp);
            _moon.SunDirection = (Vector3F)_ephemeris.SunPosition.Normalized;
#else
            Vector3F  sunRotationAxis = new Vector3F(0, -0.1f, 1).Normalized;
            float     hour            = (float)_time.TimeOfDay.TotalHours / 24;
            Matrix33F sunRotation     = Matrix33F.CreateRotation(sunRotationAxis, hour * ConstantsF.TwoPi - ConstantsF.PiOver2);

            _starfield.Orientation = sunRotation;
            _sun.Direction         = sunRotation * new Vector3F(1, 0, 0);
            _moon.SunDirection     = _sun.Direction;
#endif

            _milkyWaySkybox.PoseWorld = new Pose(
                (Matrix33F)_ephemeris.EquatorialToWorld.Minor
                * Matrix33F.CreateRotationZ(ConstantsF.PiOver2)
                * Matrix33F.CreateRotationX(ConstantsF.PiOver2));

            _moon.LookAt(moonDirection, moonUp);
            _cieSkyFilter.SunDirection       = sunDirection;
            _gradientSky.SunDirection        = sunDirection;
            _gradientTextureSky.SunDirection = sunDirection;
            _gradientTextureSky.TimeOfDay    = _time.TimeOfDay;
            _scatteringSky.SunDirection      = sunDirection;

            _cloudLayerNode.SunDirection = _scatteringSky.SunDirection;
            _cloudLayerNode.SunLight     = ChangeSaturation(_scatteringSky.GetSunlight() / 5f, 1);
            //_cloudPlaneRenderer.Color = new Vector4F(ChangeSaturation(_scatteringSky.GetFogColor(128), 0.9f) * 1.0f, 1);
            //Vector3F c = (_scatteringSky.GetFogColor(128) + _scatteringSky.GetSunlight() / 10) / 2;
            //_cloudPlaneRenderer.Color = new Vector4F(c, 1);
            _cloudLayerNode.AmbientLight = _scatteringSky.GetAmbientLight(1024) / 6f;
            //_cloudLayerNode.AmbientLight = _scatteringSky.GetFogColor(128) * _scatteringSky.GetAmbientLight(256).Length / 6f;
        }
예제 #10
0
        public void QuaternionFromMatrix33()
        {
            Vector3F    v = Vector3F.One;
            Matrix33F   m = Matrix33F.Identity;
            QuaternionF q = QuaternionF.CreateRotation(m);

            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = Matrix33F.CreateRotation(Vector3F.UnitX, 0.3f);
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = Matrix33F.CreateRotation(Vector3F.UnitY, 1.0f);
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = Matrix33F.CreateRotation(Vector3F.UnitZ, 4.0f);
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = Matrix33F.Identity;
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = Matrix33F.CreateRotation(-Vector3F.UnitX, 1.3f);
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = Matrix33F.CreateRotation(-Vector3F.UnitY, -1.4f);
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = Matrix33F.CreateRotation(-Vector3F.UnitZ, -0.1f);
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = new Matrix33F(0, 0, 1,
                              0, -1, 0,
                              1, 0, 0);
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));

            m = new Matrix33F(-1, 0, 0,
                              0, 1, 0,
                              0, 0, -1);
            q = QuaternionF.CreateRotation(m);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(m * v, q.Rotate(v)));
        }
예제 #11
0
        // In this method, a vector is rotated with a quaternion and a matrix. The result
        // of the two vector rotations are compared.
        private void RotateVector()
        {
            var debugRenderer = GraphicsScreen.DebugRenderer2D;

            debugRenderer.DrawText("----- RotateVector Example:");

            // Create a vector. We will rotate this vector.
            Vector3F v = new Vector3F(1, 2, 3);

            // Create another vector which defines the axis of a rotation.
            Vector3F rotationAxis = Vector3F.UnitZ;

            // The rotation angle in radians. We want to rotate 50°.
            float rotationAngle = MathHelper.ToRadians(50);

            // ----- Part 1: Rotate a vector with a quaternion.

            // Create a quaternion that represents a 50° rotation around the axis given
            // by rotationAxis.
            QuaternionF rotation = QuaternionF.CreateRotation(rotationAxis, rotationAngle);

            // Rotate the vector v using the rotation quaternion.
            Vector3F vRotated = rotation.Rotate(v);

            // ----- Part 2: Rotate a vector with a matrix.

            // Create a matrix that represents a 50° rotation around the axis given by
            // rotationAxis.
            Matrix33F rotationMatrix = Matrix33F.CreateRotation(rotationAxis, rotationAngle);

            // Rotate the vector v using the rotation matrix.
            Vector3F vRotated2 = rotationMatrix * v;

            // ----- Part 3: Compare the results.
            // The result of both rotations should be identical.
            // Because of numerical errors there can be minor differences in the results.
            // Therefore we use Vector3F.AreNumericallyEqual() two check if the results
            // are equal (within a sensible numerical tolerance).
            if (Vector3F.AreNumericallyEqual(vRotated, vRotated2))
            {
                debugRenderer.DrawText("Vectors are equal.\n"); // This message is written.
            }
            else
            {
                debugRenderer.DrawText("Vectors are not equal.\n");
            }
        }
예제 #12
0
        public void ToLocal()
        {
            Vector3F point0 = new Vector3F(10, 20, -40);
            Vector3F point1 = new Vector3F(-22, 34, 45);
            Line     line   = new Line(point0, (point1 - point0).Normalized);

            Pose pose = new Pose(new Vector3F(-5, 100, -20), Matrix33F.CreateRotation(new Vector3F(1, 2, 3), 0.123f));

            point0 = pose.ToLocalPosition(point0);
            point1 = pose.ToLocalPosition(point1);
            line.ToLocal(ref pose);

            Vector3F dummy;

            Assert.IsTrue(GeometryHelper.GetClosestPoint(line, point0, out dummy));
            Assert.IsTrue(GeometryHelper.GetClosestPoint(line, point1, out dummy));
        }
예제 #13
0
        public void Division()
        {
            float       angle1 = 0.4f;
            Vector3F    axis1  = new Vector3F(1.0f, 2.0f, 3.0f);
            QuaternionF q1     = QuaternionF.CreateRotation(axis1, angle1);
            Matrix33F   m1     = Matrix33F.CreateRotation(axis1, angle1);

            float       angle2 = -1.6f;
            Vector3F    axis2  = new Vector3F(1.0f, -2.0f, -3.5f);
            QuaternionF q2     = QuaternionF.CreateRotation(axis2, angle2);
            Matrix33F   m2     = Matrix33F.CreateRotation(axis2, angle2);

            Vector3F v       = new Vector3F(0.3f, -2.4f, 5.6f);
            Vector3F result1 = QuaternionF.Divide(q2, q1).Rotate(v);
            Vector3F result2 = m2 * m1.Inverse * v;

            Assert.IsTrue(Vector3F.AreNumericallyEqual(result1, result2));
        }
예제 #14
0
        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);
        }
예제 #15
0
        public void Axis()
        {
            Vector3F axis  = new Vector3F(1.0f, 2.0f, 3.0f);
            float    angle = 0.2f;

            QuaternionF q = QuaternionF.CreateRotation(axis, angle);

            Assert.IsTrue(Numeric.AreEqual(angle, q.Angle));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(axis.Normalized, q.Axis));
            axis   = new Vector3F(1.0f, 1.0f, 1.0f);
            q.Axis = axis;
            Assert.IsTrue(Numeric.AreEqual(angle, q.Angle));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(axis.Normalized, q.Axis));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(Matrix33F.CreateRotation(axis, angle) * Vector3F.One, q.Rotate(Vector3F.One)));

            Assert.AreEqual(Vector3F.Zero, QuaternionF.Identity.Axis);
            q.Axis = Vector3F.Zero;
            Assert.AreEqual(QuaternionF.Identity, q);
        }
예제 #16
0
        public override void Update(GameTime gameTime)
        {
            var debugRenderer = GraphicsScreen.DebugRenderer;

            var cd = new CollisionDetection();


            float sizeB = _part2.GeometricObject.Aabb.Extent.Length;
            var   perturbationEpsilon = sizeB * Math.Max(0.001f / 10, Numeric.EpsilonF * 10);
            var   perturbationAngle   = perturbationEpsilon / sizeB;

            Vector3F v;

            v = new Vector3F(1, 0, 0);

            var translation = v * perturbationEpsilon;
            var rotation    = Matrix33F.CreateRotation(v, perturbationAngle);

            var poseB    = _part2.GeometricObject.Pose;
            var origPose = poseB;

            poseB.Position    = translation + poseB.Position;
            poseB.Orientation = rotation * poseB.Orientation;
            //((GeometricObject)_part2.GeometricObject).Pose = poseB;


            var cp1 = cd.GetClosestPoints(_part1A, _part2);

            //var cp2 = cd.GetClosestPoints(_part1B, _part2);

            ((GeometricObject)_part2.GeometricObject).Pose = origPose;

            debugRenderer.Clear();
            debugRenderer.DrawObject(_part1A.GeometricObject, Color.DarkGreen, true, false);
            //debugRenderer.DrawObject(_part1B.GeometricObject, Color.DarkBlue, false, false);
            debugRenderer.DrawObject(_part2.GeometricObject, Color.DarkViolet, true, false);

            debugRenderer.DrawContact(cp1[0], 0.1f, Color.Yellow, true);
            //debugRenderer.DrawContact(cp2[0], 0.1f, Color.Pink, true);
        }
예제 #17
0
        public override void Update(GameTime gameTime)
        {
            // Reflect velocity if objects collide:
            // The collision domain contains a ContactSet for each pair of touching objects.
            foreach (var contactSet in _domain.ContactSets)
            {
                // Get the touching objects.
                var objectA = (MovingGeometricObject)contactSet.ObjectA.GeometricObject;
                var objectB = (MovingGeometricObject)contactSet.ObjectB.GeometricObject;

                // In rare cases, the collision detection cannot compute a contact because of
                // numerical problems. Ignore this case, we usually get a contact in the next frame.
                if (contactSet.Count == 0)
                {
                    continue;
                }

                // Get the contact normal of the first collision point.
                var      contact = contactSet[0];
                Vector3F normal  = contact.Normal;

                // Check if the objects move towards or away from each other in the direction of the normal.
                if (Vector3F.Dot(objectB.LinearVelocity - objectA.LinearVelocity, normal) <= 0)
                {
                    // Objects move towards each other. --> Reflect their velocities.
                    objectA.LinearVelocity -= 2 * Vector3F.ProjectTo(objectA.LinearVelocity, normal);
                    objectB.LinearVelocity -= 2 * Vector3F.ProjectTo(objectB.LinearVelocity, normal);
                    objectA.AngularVelocity = -objectA.AngularVelocity;
                    objectB.AngularVelocity = -objectB.AngularVelocity;
                }
            }

            // Get the size of the current time step.
            float timeStep = (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Move objects.
            var objects = _domain.CollisionObjects.Select(co => co.GeometricObject).OfType <MovingGeometricObject>();

            foreach (var obj in objects)
            {
                // Update position.
                Vector3F position = obj.Pose.Position + obj.LinearVelocity * timeStep;

                // Update rotation.
                Vector3F  rotationAxis = obj.AngularVelocity;
                float     angularSpeed = obj.AngularVelocity.Length;
                Matrix33F rotation     = (Numeric.IsZero(angularSpeed))
          ? Matrix33F.Identity
          : Matrix33F.CreateRotation(rotationAxis, angularSpeed * timeStep);
                var orientation = rotation * obj.Pose.Orientation;

                // Incrementally updating the rotation matrix will eventually create a
                // matrix which is not a rotation matrix anymore because of numerical
                // problems. Re-othogonalization make sure that the matrix represents a
                // rotation.
                orientation.Orthogonalize();

                obj.Pose = new Pose(position, orientation);
            }

            // Update collision domain. This computes new contact information.
            _domain.Update(timeStep);

            // Record some statistics.
            int numberOfObjects = _domain.CollisionObjects.Count;

            Profiler.AddValue("NumObjects", numberOfObjects);

            // If there are n objects, we can have max. n * (n - 1) / 2 collisions.
            Profiler.AddValue("NumObjectPairs", numberOfObjects * (numberOfObjects - 1f) / 2f);

            // The first part of the collision detection is the "broad-phase" which
            // filters out objects that cannot collide (e.g. using a fast bounding box test).
            Profiler.AddValue("BroadPhasePairs", _domain.NumberOfBroadPhaseOverlaps);

            // Finally, the collision detection computes the exact contact information and creates
            // a ContactSet with the Contacts for each pair of colliding objects.
            Profiler.AddValue("ContactSetCount", _domain.ContactSets.Count);

            // Draw objects using the DebugRenderer of the graphics screen.
            var debugRenderer = GraphicsScreen.DebugRenderer;

            debugRenderer.Clear();
            foreach (var collisionObject in _domain.CollisionObjects)
            {
                debugRenderer.DrawObject(collisionObject.GeometricObject, GraphicsHelper.GetUniqueColor(collisionObject), false, false);
            }
        }
예제 #18
0
        public void CreateRotation()
        {
            QuaternionF q;

            // From matrix vs. from angle/axis
            Matrix33F m = Matrix33F.CreateRotation(Vector3F.UnitX, (float)Math.PI / 4);

            q = QuaternionF.CreateRotation(m);
            QuaternionF q2 = QuaternionF.CreateRotation(Vector3F.UnitX, (float)Math.PI / 4);

            Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, q));
            m  = Matrix33F.CreateRotation(Vector3F.UnitY, (float)Math.PI / 4);
            q  = QuaternionF.CreateRotation(m);
            q2 = QuaternionF.CreateRotation(Vector3F.UnitY, (float)Math.PI / 4);
            Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, q));
            m  = Matrix33F.CreateRotation(Vector3F.UnitZ, (float)Math.PI / 4);
            q  = QuaternionF.CreateRotation(m);
            q2 = QuaternionF.CreateRotation(Vector3F.UnitZ, (float)Math.PI / 4);
            Assert.IsTrue(QuaternionF.AreNumericallyEqual(q2, q));

            // From vector-vector
            Vector3F start, end;

            start = Vector3F.UnitX;
            end   = Vector3F.UnitY;
            q     = QuaternionF.CreateRotation(start, end);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start));

            start = Vector3F.UnitY;
            end   = Vector3F.UnitZ;
            q     = QuaternionF.CreateRotation(start, end);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start));

            start = Vector3F.UnitZ;
            end   = Vector3F.UnitX;
            q     = QuaternionF.CreateRotation(start, end);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start));

            start = new Vector3F(1, 1, 1);
            end   = new Vector3F(1, 1, 1);
            q     = QuaternionF.CreateRotation(start, end);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start));

            start = new Vector3F(1.0f, 1.0f, 1.0f);
            end   = new Vector3F(-1.0f, -1.0f, -1.0f);
            q     = QuaternionF.CreateRotation(start, end);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start));

            start = new Vector3F(-1.0f, 2.0f, 1.0f);
            end   = new Vector3F(-2.0f, -1.0f, -1.0f);
            q     = QuaternionF.CreateRotation(start, end);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(end, q.ToRotationMatrix33() * start));

            float degree45 = MathHelper.ToRadians(45);

            q = QuaternionF.CreateRotation(degree45, Vector3F.UnitZ, degree45, Vector3F.UnitY, degree45, Vector3F.UnitX, false);
            QuaternionF expected = QuaternionF.CreateRotation(Vector3F.UnitZ, degree45) * QuaternionF.CreateRotation(Vector3F.UnitY, degree45)
                                   * QuaternionF.CreateRotation(Vector3F.UnitX, degree45);

            Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, q));

            q        = QuaternionF.CreateRotation(degree45, Vector3F.UnitZ, degree45, Vector3F.UnitY, degree45, Vector3F.UnitX, true);
            expected = QuaternionF.CreateRotation(Vector3F.UnitX, degree45) * QuaternionF.CreateRotation(Vector3F.UnitY, degree45)
                       * QuaternionF.CreateRotation(Vector3F.UnitZ, degree45);
            Assert.IsTrue(QuaternionF.AreNumericallyEqual(expected, q));
        }
예제 #19
0
 public void CreateRotationException()
 {
     Matrix33F.CreateRotation(Vector3F.Zero, 1f);
 }
예제 #20
0
        // Render outline of sphere in perspective projection.
        private void RenderSphereOutline(float radius, ref Vector3F center, ref Pose cameraPose, ref Vector3F right, ref Color color)
        {
            // TODO: Try alternative algorithm. See http://www.gamasutra.com/view/feature/131351/the_mechanics_of_robust_stencil_.php?page=6

            // Forward direction of camera in world space.
            Vector3F forward = cameraPose.Orientation * Vector3F.Forward;

            // Vector pointing from camera position to center of sphere.
            Vector3F cameraToCenter       = center - cameraPose.Position;
            float    cameraToCenterLength = cameraToCenter.Length;

            if (Numeric.IsLessOrEqual(cameraToCenterLength, radius))
            {
                // Camera is within sphere.
                return;
            }

            cameraToCenter /= cameraToCenterLength;

            // ----- Next, find the outline of the sphere, which is visible from the camera.

            // We need to find cameraToPoint (the line from the camera to a point on the outline).
            // cameraToPoint (= adjacent), radius (= opposite) and cameraToCenter (= hypotenuse)
            // form a right-angled triangle.
            // α is the angle between cameraToPoint and cameraToCenter:
            float α = (float)Math.Asin(radius / cameraToCenterLength);

            // Determine the normal of the triangle.
            Vector3F normal = Vector3F.Cross(right, cameraToCenter);

            if (normal.IsNumericallyZero)
            {
                normal = Vector3F.Up;
            }

            // adjacent = √(hypotenuse² + opposite²)
            float    radiusSquared = radius * radius;
            float    cameraToCenterLengthSquared = cameraToCenterLength * cameraToCenterLength;
            float    cameraToPointLength         = (float)Math.Sqrt(cameraToCenterLengthSquared - radiusSquared);
            Vector3F cameraToPoint = Matrix33F.CreateRotation(normal, α) * cameraToCenter;

            // Get the first point on the outline in world space.
            Vector3F pStart = cameraPose.Position + cameraToPoint * cameraToPointLength;

            // Incrementally rotate the right vector 360° and repeat to find the remaining
            // points on the outline.
            const int NumberOfSegments = 32;

            for (int i = 1; i <= NumberOfSegments; i++)
            {
                float β = i * ConstantsF.TwoPi / NumberOfSegments;

                Vector3F rightRotated = Matrix33F.CreateRotation(forward, -β) * right;
                normal = Vector3F.Cross(rightRotated, cameraToCenter);
                if (normal.IsNumericallyZero)
                {
                    normal = Vector3F.Up;
                }

                cameraToPointLength = (float)Math.Sqrt(cameraToCenterLengthSquared - radiusSquared);
                cameraToPoint       = Matrix33F.CreateRotation(normal, α) * cameraToCenter;
                Vector3F pEnd = cameraPose.Position + cameraToPoint * cameraToPointLength;
                _lineBatch.Add(pStart, pEnd, color);
                pStart = pEnd;
            }
        }
예제 #21
0
        // See FAST paper: "Interactive Continuous Collision Detection for Non-Convex Polyhedra", Kim Young et al.

        /// <summary>
        /// Gets the time of impact using Conservative Advancement.
        /// </summary>
        /// <param name="objectA">The object A.</param>
        /// <param name="targetPoseA">The target pose of A.</param>
        /// <param name="objectB">The object B.</param>
        /// <param name="targetPoseB">The target pose of B.</param>
        /// <param name="allowedPenetrationDepth">The allowed penetration depth.</param>
        /// <param name="collisionDetection">The collision detection.</param>
        /// <returns>
        /// The time of impact in the range [0, 1].
        /// </returns>
        /// <remarks>
        /// This algorithm does not work for concave objects.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="objectA"/>, <paramref name="objectB"/> or
        /// <paramref name="collisionDetection"/> is <see langword="null"/>.
        /// </exception>
        internal static float GetTimeOfImpactCA(CollisionObject objectA, Pose targetPoseA,
                                                CollisionObject objectB, Pose targetPoseB, float allowedPenetrationDepth,
                                                CollisionDetection collisionDetection) // Required for collision algorithm matrix.
        {
            if (objectA == null)
            {
                throw new ArgumentNullException("objectA");
            }
            if (objectB == null)
            {
                throw new ArgumentNullException("objectB");
            }
            if (collisionDetection == null)
            {
                throw new ArgumentNullException("collisionDetection");
            }

            IGeometricObject geometricObjectA = objectA.GeometricObject;
            IGeometricObject geometricObjectB = objectB.GeometricObject;

            Pose startPoseA = geometricObjectA.Pose;
            Pose startPoseB = geometricObjectB.Pose;

            // Get angular velocity ω of object A (as magnitude + rotation axis).

            // qEnd = ∆q * qStart
            // => ∆q = qEnd * qStart.Inverse
            QuaternionF qA = QuaternionF.CreateRotation(targetPoseA.Orientation * startPoseA.Orientation.Transposed);

            // ω = ∆α / ∆t, ∆t = 1
            // => ω = ∆α
            float    ωA     = qA.Angle;                                                // Magnitude |ω|
            Vector3F ωAxisA = (!Numeric.AreEqual(qA.W, 1)) ? qA.Axis : Vector3F.UnitX; // Rotation axis of ω

            // Get angular velocity ω of object B (as magnitude + rotation axis).
            // (Same as above.)
            QuaternionF qB     = QuaternionF.CreateRotation(targetPoseB.Orientation * startPoseB.Orientation.Transposed);
            float       ωB     = qB.Angle;                                                // Magnitude |ω|
            Vector3F    ωAxisB = (!Numeric.AreEqual(qB.W, 1)) ? qB.Axis : Vector3F.UnitX; // Rotation axis of ω

            // Bounding sphere radii.
            float rMaxA = GetBoundingRadius(geometricObjectA);
            float rMaxB = GetBoundingRadius(geometricObjectB);

            // |ω| * rMax is the angular part of the projected velocity bound.
            float angularVelocityProjected = ωA * rMaxA + ωB * rMaxB;

            // Compute relative linear velocity.
            // (linearRelVel ∙ normal > 0 if objects are getting closer.)
            Vector3F linearVelocityA        = targetPoseA.Position - startPoseA.Position;
            Vector3F linearVelocityB        = targetPoseB.Position - startPoseB.Position;
            Vector3F linearVelocityRelative = linearVelocityA - linearVelocityB;

            // Abort if relative movement is zero.
            if (Numeric.IsZero(linearVelocityRelative.Length + angularVelocityProjected))
            {
                return(1);
            }

            var distanceAlgorithm = collisionDetection.AlgorithmMatrix[objectA, objectB];

            // Use temporary test objects.
            var testGeometricObjectA = TestGeometricObject.Create();

            testGeometricObjectA.Shape = geometricObjectA.Shape;
            testGeometricObjectA.Scale = geometricObjectA.Scale;
            testGeometricObjectA.Pose  = startPoseA;

            var testGeometricObjectB = TestGeometricObject.Create();

            testGeometricObjectB.Shape = geometricObjectB.Shape;
            testGeometricObjectB.Scale = geometricObjectB.Scale;
            testGeometricObjectB.Pose  = startPoseB;

            var testCollisionObjectA = ResourcePools.TestCollisionObjects.Obtain();

            testCollisionObjectA.SetInternal(objectA, testGeometricObjectA);

            var testCollisionObjectB = ResourcePools.TestCollisionObjects.Obtain();

            testCollisionObjectB.SetInternal(objectB, testGeometricObjectB);

            var testContactSet = ContactSet.Create(testCollisionObjectA, testCollisionObjectB);

            try
            {
                distanceAlgorithm.UpdateClosestPoints(testContactSet, 0);

                if (testContactSet.Count < 0)
                {
                    // No distance result --> Abort.
                    return(1);
                }

                Vector3F normal   = testContactSet[0].Normal;
                float    distance = -testContactSet[0].PenetrationDepth;

                float λ         = 0;
                float λPrevious = 0;

                for (int i = 0; i < MaxNumberOfIterations && distance > 0; i++)
                {
                    // |v∙n|
                    float linearVelocityProject = Vector3F.Dot(linearVelocityRelative, normal);

                    // |n x ω| * rMax
                    angularVelocityProjected = Vector3F.Cross(normal, ωAxisA).Length *ωA *rMaxA
                                               + Vector3F.Cross(normal, ωAxisB).Length *ωB *rMaxB;

                    // Total projected velocity.
                    float velocityProjected = linearVelocityProject + angularVelocityProjected;

                    // Abort for separating objects.
                    if (Numeric.IsLess(velocityProjected, 0))
                    {
                        break;
                    }

                    // Increase TOI.
                    float μ = (distance + allowedPenetrationDepth) / velocityProjected;
                    λ = λ + μ;

                    if (λ < 0 || λ > 1)
                    {
                        break;
                    }

                    Debug.Assert(λPrevious < λ);

                    if (λ <= λPrevious)
                    {
                        break;
                    }

                    // Get new interpolated poses.
                    Vector3F  positionA = startPoseA.Position + λ * (targetPoseA.Position - startPoseA.Position);
                    Matrix33F rotationA = Matrix33F.CreateRotation(ωAxisA, λ * ωA);
                    testGeometricObjectA.Pose = new Pose(positionA, rotationA * startPoseA.Orientation);

                    Vector3F  positionB = startPoseB.Position + λ * (targetPoseB.Position - startPoseB.Position);
                    Matrix33F rotationB = Matrix33F.CreateRotation(ωAxisB, λ * ωB);
                    testGeometricObjectB.Pose = new Pose(positionB, rotationB * startPoseB.Orientation);

                    // Get new closest point distance.
                    distanceAlgorithm.UpdateClosestPoints(testContactSet, 0);
                    if (testContactSet.Count == 0)
                    {
                        break;
                    }

                    normal   = testContactSet[0].Normal;
                    distance = -testContactSet[0].PenetrationDepth;

                    λPrevious = λ;
                }

                if (testContactSet.HaveContact && λ > 0 && λ < 1 && testContactSet.Count > 0)
                {
                    return(λ);
                    // We already have a contact that we could use.
                    // result.Contact = testContactSet[0];
                }
            }
            finally
            {
                // Recycle temporary objects.
                testContactSet.Recycle(true);
                ResourcePools.TestCollisionObjects.Recycle(testCollisionObjectA);
                ResourcePools.TestCollisionObjects.Recycle(testCollisionObjectB);
                testGeometricObjectA.Recycle();
                testGeometricObjectB.Recycle();
            }

            return(1);
        }
예제 #22
0
        public override void Update(GameTime gameTime)
        {
            _domain.EnableMultithreading = InputService.IsDown(Keys.Space);

#if PROFILE
            MessageBox.Show("Start");
            _deltaTime = 1 / 60f;
            for (int bla = 0; bla < 10000; bla++)
            {
#endif
            if (ClosestPointQueriesEnabled)
            {
                // Here we run closest point queries on all object pairs.
                // We compare the results with the contact queries.

                for (int i = 0; i < _domain.CollisionObjects.Count; i++)
                {
                    for (int j = i + 1; j < _domain.CollisionObjects.Count; j++)
                    {
                        CollisionObject a = _domain.CollisionObjects[i];
                        CollisionObject b = _domain.CollisionObjects[j];

                        ContactSet closestPointQueryResult = _domain.CollisionDetection.GetClosestPoints(a, b);
                        ContactSet contactSet = _domain.GetContacts(a, b);

                        // Ignore height fields and rays.
                        if (a.GeometricObject.Shape is HeightField || b.GeometricObject.Shape is HeightField)
                        {
                            break;
                        }
                        if (a.GeometricObject.Shape is RayShape || b.GeometricObject.Shape is RayShape)
                        {
                            break;
                        }

                        if (contactSet == null || !contactSet.HaveContact)
                        {
                            // No contact in contactSet
                            if (closestPointQueryResult.HaveContact)
                            {
                                // Contact in closest point query. Results are inconsistent.
                                if (closestPointQueryResult.Count > 0 &&
                                    closestPointQueryResult[0].PenetrationDepth > 0.001f)
                                {
                                    Debugger.Break();
                                }
                            }
                        }
                        else if (!closestPointQueryResult.HaveContact)
                        {
                            // contact in contact query, but no contact in closest point query.
                            // We allow a deviation within a small tolerance.
                            if (closestPointQueryResult.Count > 0 && contactSet.Count > 0 &&
                                closestPointQueryResult[0].PenetrationDepth + contactSet[0].PenetrationDepth > 0.001f)
                            {
                                Debugger.Break();
                            }
                        }
                    }
                }
            }

            // Reflect velocity if objects collide:
            // The collision domain contains a ContactSet for each pair of touching objects.
            foreach (var contactSet in _domain.ContactSets)
            {
                // Get the touching objects.
                var moA = (MovingGeometricObject)contactSet.ObjectA.GeometricObject;
                var moB = (MovingGeometricObject)contactSet.ObjectB.GeometricObject;

                // Reflect only at boundary objects.
                if (!(moA.Shape is PlaneShape) && !(moB.Shape is PlaneShape) &&
                    !(moA.Shape is HeightField) && !(moB.Shape is HeightField))
                {
                    continue;
                }

                // Get normal vector. If objects are sensors, the contact set does not tell us
                // the right normal.
                Vector3F normal = Vector3F.Zero;
                if (contactSet.Count > 0)
                {
                    // Take normal from contact set.
                    normal = contactSet[0].Normal;
                }
                else
                {
                    // If we use Trigger CollisionObjects we do not have contacts. --> Reflect at
                    // bounding planes.
                    if (moA.Shape is PlaneShape)
                    {
                        normal = ((PlaneShape)moA.Shape).Normal;
                    }
                    else if (moB.Shape is PlaneShape)
                    {
                        normal = -((PlaneShape)moB.Shape).Normal;
                    }
                    else if (moA.Shape is HeightField)
                    {
                        normal = Vector3F.UnitY;
                    }
                    else
                    {
                        normal = -Vector3F.UnitY;
                    }
                }
                //else if (moA.Shape is Plane || moB.Shape is Plane                       )
                //{
                //  // Use plane normal.
                //  IGeometricObject plane = moA.Shape is Plane ? moA : moB;
                //  normal = plane.Pose.ToWorldDirection(((Plane)plane.Shape).Normal);
                //  if (moB == plane)
                //    normal = -normal;
                //}
                //else if (moA.Shape is HeightField || moB.Shape is HeightField)
                //{
                //  // Use up-vector for height field contacts.
                //  normal = Vector3F.UnitY;
                //  if (moB.Shape is HeightField)
                //    normal = -normal;
                //}
                //else
                //{
                //  // Use random normal.
                //  normal = RandomHelper.NextVector3F(-1, 1).Normalized;
                //}

                // Check if the objects move towards or away from each other in the direction of the normal.
                if (normal != Vector3F.Zero && Vector3F.Dot(moB.LinearVelocity - moA.LinearVelocity, normal) <= 0)
                {
                    // Objects move towards each other. --> Reflect their velocities.
                    moA.LinearVelocity -= 2 * Vector3F.ProjectTo(moA.LinearVelocity, normal);
                    moB.LinearVelocity -= 2 * Vector3F.ProjectTo(moB.LinearVelocity, normal);
                    moA.AngularVelocity = -moA.AngularVelocity;
                    moB.AngularVelocity = -moB.AngularVelocity;
                }
            }

            // Get the size of the current time step.
            float timeStep = (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Move objects.
            var objects = _domain.CollisionObjects.Select(co => co.GeometricObject).OfType <MovingGeometricObject>();
            foreach (var obj in objects)
            {
                // Update position.
                Vector3F position = obj.Pose.Position + obj.LinearVelocity * timeStep;

                // Update rotation.
                Vector3F  rotationAxis = obj.AngularVelocity;
                float     angularSpeed = obj.AngularVelocity.Length;
                Matrix33F rotation     = (Numeric.IsZero(angularSpeed))
          ? Matrix33F.Identity
          : Matrix33F.CreateRotation(rotationAxis, angularSpeed * timeStep);
                var orientation = rotation * obj.Pose.Orientation;

                // Incrementally updating the rotation matrix will eventually create a
                // matrix which is not a rotation matrix anymore because of numerical
                // problems. Re-othogonalization make sure that the matrix represents a
                // rotation.
                orientation.Orthogonalize();

                obj.Pose = new Pose(position, orientation);
            }



            // Update collision domain. This computes new contact information.
            _domain.Update(timeStep);

#if PROFILE
            MessageBox.Show("Finished");
            Exit();
#endif

            // Record some statistics.
            int numberOfObjects = _domain.CollisionObjects.Count;
            Profiler.SetFormat("NumObjects", 1, "The total number of objects.");
            Profiler.AddValue("NumObjects", numberOfObjects);
            // If there are n objects, we can have max. n * (n - 1) / 2 collisions.
            Profiler.SetFormat("NumObjectPairs", 1, "The number of objects pairs, which have to be checked.");
            Profiler.AddValue("NumObjectPairs", numberOfObjects * (numberOfObjects - 1f) / 2f);
            // The first part of the collision detection is the "broad-phase" which
            // filters out objects that cannot collide (e.g. using a fast bounding box test).
            Profiler.SetFormat("BroadPhasePairs", 1, "The number of overlaps reported by the broad phase.");
            Profiler.AddValue("BroadPhasePairs", _domain.NumberOfBroadPhaseOverlaps);
            // Finally, the collision detection computes the exact contact information and creates
            // a ContactSet with the Contacts for each pair of colliding objects.
            Profiler.SetFormat("ContactSetCount", 1, "The number of actual collisions.");
            Profiler.AddValue("ContactSetCount", _domain.ContactSets.Count);

            // Draw objects using the DebugRenderer of the graphics screen.
            var debugRenderer = GraphicsScreen.DebugRenderer;
            debugRenderer.Clear();
            foreach (var collisionObject in _domain.CollisionObjects)
            {
                debugRenderer.DrawObject(collisionObject.GeometricObject, GraphicsHelper.GetUniqueColor(collisionObject), false, false);
            }
        }
    }
예제 #23
0
        /// <summary>
        /// Performs more collision tests while slightly rotating one collision object.
        /// </summary>
        /// <param name="collisionDetection">The collision detection.</param>
        /// <param name="contactSet">
        /// The contact set; must contain at least 1 <see cref="Contact"/>.
        /// </param>
        /// <param name="perturbB">
        /// if set to <see langword="true"/> collision object B will be rotated; otherwise collision
        /// object A will be rotated.
        /// </param>
        /// <param name="testMethod">The test method that is called to compute contacts.</param>
        /// <remarks>
        /// This method rotates one object 3 times and calls contact computation for the new
        /// orientations. It is recommended to call this method only when the contact set has 1 new
        /// contact.
        /// </remarks>
        internal static void TestWithPerturbations(CollisionDetection collisionDetection, ContactSet contactSet, bool perturbB, Action <ContactSet> testMethod)
        {
            Debug.Assert(contactSet != null);
            Debug.Assert(contactSet.Count > 0 && contactSet.HaveContact || !contactSet.IsPerturbationTestAllowed);
            Debug.Assert(testMethod != null);

            // Make this test only if there is 1 contact.
            // If there are 0 contacts, we assume that the contact pair is separated.
            // If there are more than 3 contacts, then we already have a lot of contacts to work with, no
            // need to search for more.
            if (!contactSet.HaveContact || contactSet.Count == 0 || contactSet.Count >= 4 || !contactSet.IsPerturbationTestAllowed)
            {
                return;
            }

            // Get data of object that will be rotated.
            var collisionObject = (perturbB) ? contactSet.ObjectB : contactSet.ObjectA;
            var geometricObject = collisionObject.GeometricObject;
            var pose            = geometricObject.Pose;

            // Get normal, pointing to the test object.
            var normal = contactSet[0].Normal;

            if (!perturbB)
            {
                normal = -normal;
            }

            var contactPosition = contactSet[0].Position;
            var centerToContact = contactPosition - pose.Position;

            // Compute a perturbation angle proportional to the dimension of the object.
            var radius = geometricObject.Aabb.Extent.Length;
            var angle  = collisionDetection.ContactPositionTolerance / radius;

            // axis1 is in the contact tangent plane, orthogonal to normal.
            var axis1 = Vector3F.Cross(normal, centerToContact);

            // If axis1 is zero then normal and centerToContact are collinear. This happens
            // for example for spheres or cone tips against flat faces. In these cases we assume
            // that there will be max. 1 contact.
            if (axis1.IsNumericallyZero)
            {
                return;
            }

            var axis1Local = pose.ToLocalDirection(axis1);
            var rotation   = Matrix33F.CreateRotation(axis1Local, -angle);

            // Use temporary test objects.
            var testGeometricObject = TestGeometricObject.Create();

            testGeometricObject.Shape = geometricObject.Shape;
            testGeometricObject.Scale = geometricObject.Scale;
            testGeometricObject.Pose  = new Pose(pose.Position, pose.Orientation * rotation);

            var testCollisionObject = ResourcePools.TestCollisionObjects.Obtain();

            testCollisionObject.SetInternal(collisionObject, testGeometricObject);

            var testContactSet = perturbB ? ContactSet.Create(contactSet.ObjectA, testCollisionObject)
                                    : ContactSet.Create(testCollisionObject, contactSet.ObjectB);

            testContactSet.IsPerturbationTestAllowed = false;       // Avoid recursive perturbation tests!
            testContactSet.PreferredNormal           = contactSet.PreferredNormal;

            // Compute next contacts.
            testMethod(testContactSet);

            if (testContactSet.Count > 0)
            {
                // axis2 is in the contact tangent plane, orthogonal to axis1.
                var axis2      = Vector3F.Cross(axis1, normal);
                var axis2Local = pose.ToLocalDirection(axis2);

                var rotation2 = Matrix33F.CreateRotation(axis2Local, -angle);
                testGeometricObject.Pose = new Pose(pose.Position, pose.Orientation * rotation2);

                // Compute next contacts.
                testMethod(testContactSet);

                // Invert rotation2.
                rotation2.Transpose();
                testGeometricObject.Pose = new Pose(pose.Position, pose.Orientation * rotation2);

                // Compute next contacts.
                testMethod(testContactSet);
            }

            // Set HaveContact. It is reset when a perturbation separates the objects.
            testContactSet.HaveContact = true;

            // TODO: Test if we need this:
            // The contact world positions are not really correct because one object was rotated.
            // UpdateContacts recomputes the world positions from the local positions.
            UpdateContacts(testContactSet, 0, collisionDetection.ContactPositionTolerance);

            // Merge contacts of testContactSet into contact set, but do not change existing contacts.
            foreach (var contact in testContactSet)
            {
                // We call TryMerge() to see if the contact is similar to an existing contact.
                bool exists = TryMergeWithNearestContact(
                    contactSet,
                    contact,
                    collisionDetection.ContactPositionTolerance,
                    false); // The existing contact must no be changed!

                if (exists)
                {
                    // We can throw away the new contact because a similar is already in the contact set.
                    contact.Recycle();
                }
                else
                {
                    // Add new contact.
                    contactSet.Add(contact);
                }
            }

            // Recycle temporary objects.
            testContactSet.Recycle();
            ResourcePools.TestCollisionObjects.Recycle(testCollisionObject);
            testGeometricObject.Recycle();
        }