예제 #1
0
        private void Rebuild(UndoBuffer undoBuffer)
        {
            this.DebugDepth("Rebuild");

            using (RebuildLock())
            {
                var startingAabb = this.GetAxisAlignedBoundingBox();

                // remove whatever rotation has been applied (they go in reverse order)
                Matrix = Matrix4X4.Identity;

                // add the current rotation
                Matrix = this.ApplyAtPosition(startingAabb.Center, Matrix4X4.CreateRotationX(MathHelper.DegreesToRadians(RotationXDegrees)));
                Matrix = this.ApplyAtPosition(startingAabb.Center, Matrix4X4.CreateRotationY(MathHelper.DegreesToRadians(RotationYDegrees)));
                Matrix = this.ApplyAtPosition(startingAabb.Center, Matrix4X4.CreateRotationZ(MathHelper.DegreesToRadians(RotationZDegrees)));

                if (startingAabb.ZSize > 0)
                {
                    // If the part was already created and at a height, maintain the height.
                    PlatingHelper.PlaceMeshAtHeight(this, startingAabb.minXYZ.Z);
                }
            }

            Invalidate(new InvalidateArgs(this, InvalidateType.Matrix, null));
        }
예제 #2
0
        public void FrustumTransformTests()
        {
            Frustum frustum = new Frustum(
                new Plane(new Vector3(1, 0, 0), 20),
                new Plane(new Vector3(-1, 0, 0), 20),
                new Plane(new Vector3(0, 1, 0), 20),
                new Plane(new Vector3(0, -1, 0), 20),
                new Plane(new Vector3(0, 0, 1), 20),
                new Plane(new Vector3(0, 0, -1), 20));

            // moved right
            {
                Frustum movedRightFrustum = Frustum.Transform(frustum, Matrix4X4.CreateTranslation(10, 0, 0));
                Assert.IsTrue(movedRightFrustum.Planes[0] == new Plane(new Vector3(1, 0, 0), 30));
                Assert.IsTrue(movedRightFrustum.Planes[1] == new Plane(new Vector3(-1, 0, 0), 10));
                Assert.IsTrue(movedRightFrustum.Planes[2] == frustum.Planes[2]);
                Assert.IsTrue(movedRightFrustum.Planes[3] == frustum.Planes[3]);
                Assert.IsTrue(movedRightFrustum.Planes[4] == frustum.Planes[4]);
                Assert.IsTrue(movedRightFrustum.Planes[5] == frustum.Planes[5]);
            }

            // rotated right
            {
                Frustum   movedRightFrustum = Frustum.Transform(frustum, Matrix4X4.CreateRotationY(MathHelper.DegreesToRadians(45)));
                Matrix4X4 testMatrix        = Matrix4X4.CreateRotationY(MathHelper.DegreesToRadians(45));
                Plane     control           = new Plane(Vector3.TransformNormal(frustum.Planes[0].PlaneNormal, testMatrix), frustum.Planes[0].DistanceToPlaneFromOrigin);
                Assert.IsTrue(movedRightFrustum.Planes[0].Equals(control, .001, .01));
                Assert.IsTrue(movedRightFrustum.Planes[1].Equals(new Plane(Vector3.TransformNormal(frustum.Planes[1].PlaneNormal, testMatrix), frustum.Planes[1].DistanceToPlaneFromOrigin)));
                Assert.IsTrue(movedRightFrustum.Planes[2].Equals(frustum.Planes[2]));
                Assert.IsTrue(movedRightFrustum.Planes[3].Equals(frustum.Planes[3]));
                Assert.IsTrue(movedRightFrustum.Planes[4].Equals(new Plane(Vector3.TransformNormal(frustum.Planes[4].PlaneNormal, testMatrix), frustum.Planes[4].DistanceToPlaneFromOrigin)));
                Assert.IsTrue(movedRightFrustum.Planes[5].Equals(new Plane(Vector3.TransformNormal(frustum.Planes[5].PlaneNormal, testMatrix), frustum.Planes[5].DistanceToPlaneFromOrigin)));
            }
        }
예제 #3
0
        public static Vector3 MapMoveToSphere(Vector2 screenCenter, WorldView world, double trackBallRadius, Vector2 startPosition, Vector2 endPosition, bool rotateOnZ)
        {
            if (rotateOnZ)
            {
                var deltaFromScreenCenter = screenCenter - endPosition;

                var angleToTravel = screenCenter.GetDeltaAngle(startPosition, endPosition);

                // now rotate that position about z in the direction of the screen vector
                var positionOnRotationSphere = Vector3Ex.Transform(new Vector3(1, 0, 0), Matrix4X4.CreateRotationZ(angleToTravel / 2));

                return(positionOnRotationSphere);
            }
            else
            {
                var deltaFromStartPixels = endPosition - startPosition;
                var deltaOnSurface       = new Vector2(deltaFromStartPixels.X / trackBallRadius, deltaFromStartPixels.Y / trackBallRadius);

                var lengthOnSurfaceRadi = deltaOnSurface.Length;

                // get this rotation on the surface of the sphere about y
                var positionAboutY = Vector3Ex.Transform(new Vector3(0, 0, 1), Matrix4X4.CreateRotationY(lengthOnSurfaceRadi));

                // get the angle that this distance travels around the sphere
                var angleToTravel = Math.Atan2(deltaOnSurface.Y, deltaOnSurface.X);

                // now rotate that position about z in the direction of the screen vector
                var positionOnRotationSphere = Vector3Ex.Transform(positionAboutY, Matrix4X4.CreateRotationZ(angleToTravel));

                return(positionOnRotationSphere);
            }
        }
예제 #4
0
        // Transform by quaternion
        public void PlaneTransformTest2()
        {
            Plane <float> target = new Plane <float>(1, 2, 3, 4);

            target = Plane.Normalize(target);

            Matrix4X4 <float> m =
                Matrix4X4.CreateRotationX(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationY(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationZ(MathHelper.ToRadians(30.0f));
            Quaternion <float> q = Quaternion <float> .CreateFromRotationMatrix(m);

            Plane <float> expected = new Plane <float>();
            float         x = target.Normal.X, y = target.Normal.Y, z = target.Normal.Z, w = target.Distance;

            expected.Normal = new Vector3D <float>(
                x * m.M11 + y * m.M21 + z * m.M31 + w * m.M41,
                x * m.M12 + y * m.M22 + z * m.M32 + w * m.M42,
                x * m.M13 + y * m.M23 + z * m.M33 + w * m.M43);
            expected.Distance = x * m.M14 + y * m.M24 + z * m.M34 + w * m.M44;

            Plane <float> actual;

            actual = Plane.Transform(target, q);
            Assert.True(MathHelper.Equal(expected, actual), "Plane<float>.Transform did not return the expected value.");
        }
        private Vector3 MapMoveToSphere(Vector2 screenPoint, bool rotateOnZ)
        {
            if (rotateOnZ)
            {
                var deltaFromScreenCenter = world.ScreenCenter - screenPoint;

                var angleToTravel = world.ScreenCenter.GetDeltaAngle(mouseDownPosition, screenPoint);

                // now rotate that position about z in the direction of the screen vector
                var positionOnRotationSphere = Vector3.Transform(new Vector3(1, 0, 0), Matrix4X4.CreateRotationZ(angleToTravel / 2));

                return(positionOnRotationSphere);
            }
            else
            {
                var deltaFromStartPixels = screenPoint - mouseDownPosition;
                var deltaOnSurface       = new Vector2(deltaFromStartPixels.X / rotationTrackingRadiusPixels, deltaFromStartPixels.Y / rotationTrackingRadiusPixels);

                var lengthOnSurfaceRadi = deltaOnSurface.Length;

                // get this rotation on the surface of the sphere about y
                var positionAboutY = Vector3.Transform(new Vector3(0, 0, 1), Matrix4X4.CreateRotationY(lengthOnSurfaceRadi));

                // get the angle that this distance travels around the sphere
                var angleToTravel = Math.Atan2(deltaOnSurface.Y, deltaOnSurface.X);

                // now rotate that position about z in the direction of the screen vector
                var positionOnRotationSphere = Vector3.Transform(positionAboutY, Matrix4X4.CreateRotationZ(angleToTravel));

                return(positionOnRotationSphere);
            }
        }
예제 #6
0
        // Transform by matrix
        public void PlaneTransformTest1()
        {
            Plane <float> target = new Plane <float>(1, 2, 3, 4);

            target = Plane.Normalize(target);

            Matrix4X4 <float> m =
                Matrix4X4.CreateRotationX(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationY(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationZ(MathHelper.ToRadians(30.0f));

            m.M41 = 10.0f;
            m.M42 = 20.0f;
            m.M43 = 30.0f;

            Plane <float>     expected = new Plane <float>();
            Matrix4X4 <float> inv;

            Matrix4X4.Invert(m, out inv);
            Matrix4X4 <float> itm = Matrix4X4.Transpose(inv);
            float             x = target.Normal.X, y = target.Normal.Y, z = target.Normal.Z, w = target.Distance;

            expected.Normal = new Vector3D <float>(
                x * itm.M11 + y * itm.M21 + z * itm.M31 + w * itm.M41,
                x * itm.M12 + y * itm.M22 + z * itm.M32 + w * itm.M42,
                x * itm.M13 + y * itm.M23 + z * itm.M33 + w * itm.M43);
            expected.Distance = x * itm.M14 + y * itm.M24 + z * itm.M34 + w * itm.M44;

            Plane <float> actual;

            actual = Plane.Transform(target, m);
            Assert.True(MathHelper.Equal(expected, actual), "Plane<float>.Transform did not return the expected value.");
        }
예제 #7
0
        public void RotateAbsolute(double x, double y, double z)
        {
            Matrix4X4 M1 = Matrix4X4.Identity;
            Matrix4X4 M2 = Matrix4X4.Identity;
            Matrix4X4 M3 = Matrix4X4.Identity;
            Matrix4X4 M4 = Matrix4X4.Identity;
            Vector3   save1;

            // save the old position
            save1.x = AxisToWorld[3, 0];
            save1.y = AxisToWorld[3, 1];
            save1.z = AxisToWorld[3, 2];

            M1 = Matrix4X4.CreateRotationX(x);
            M2 = Matrix4X4.CreateRotationY(y);
            M3 = Matrix4X4.CreateRotationZ(z);
            // 1 * 2 * 3
            M4          = M2 * M1;
            AxisToWorld = M3 * M4;

            // stuff the old position back in
            AxisToWorld[3, 0] = save1.x;
            AxisToWorld[3, 1] = save1.y;
            AxisToWorld[3, 2] = save1.z;

            AxisToWorld = Matrix4X4.Invert(WorldToAxis);
        }
예제 #8
0
        public void Can_Rotate_Point_Around_Y()
        {
            var point       = new Point(0, 0, 1);
            var halfQuarter = Matrix4X4.CreateRotationY(Math.PI / 4);
            var fullQuarter = Matrix4X4.CreateRotationY(Math.PI / 2);

            (halfQuarter * point).ShouldBe(new Point(Math.Sqrt(2) / 2, 0, Math.Sqrt(2) / 2));
            (fullQuarter * point).ShouldBe(new Point(1, 0, 0));
        }
예제 #9
0
        public void Construct_Ray_When_Camera_Is_Transformed()
        {
            var camera = new Camera(201, 101, Math.PI / 2)
            {
                ViewTransform = Matrix4X4.CreateRotationY(Math.PI / 4) * Matrix4X4.CreateTranslation(0, -2, 5),
            };

            var ray = camera.RayForPixel(100, 50);

            ray.Origin.ShouldBe(new Point(0, 2, -5));
            ray.Direction.ShouldBe(new Vector(Math.Sqrt(2) / 2, 0, -Math.Sqrt(2) / 2));
        }
예제 #10
0
        public LogoSpinner(GuiWidget widget, double scale = 1.6, double spinSpeed = 0.6, double yOffset = 0.5, double rotateX = -0.1)
        {
            // loading animation stuff
            LightingData lighting = new LightingData();

            Mesh logoMesh;

            using (var logoStream = StaticData.Instance.OpenStream(Path.Combine("Stls", "MH Logo.stl")))
            {
                logoMesh = StlProcessing.Load(logoStream, CancellationToken.None);
            }

            // Position
            var aabb = logoMesh.GetAxisAlignedBoundingBox();

            logoMesh.Transform(Matrix4X4.CreateTranslation(-aabb.Center));

            logoMesh.Transform(Matrix4X4.CreateScale(scale / aabb.XSize));

            var anglePerDraw = 1 / MathHelper.Tau * spinSpeed;
            var angle        = 0.0;

            widget.BeforeDraw += (s, e) =>
            {
                var       screenSpaceBounds = widget.TransformToScreenSpace(widget.LocalBounds);
                WorldView world             = new WorldView(screenSpaceBounds.Width, screenSpaceBounds.Height);
                world.Translate(new Vector3(0, yOffset, 0));
                world.Rotate(Quaternion.FromEulerAngles(new Vector3(rotateX, 0, 0)));

                GLHelper.SetGlContext(world, screenSpaceBounds, lighting);
                GLHelper.Render(logoMesh, this.MeshColor, Matrix4X4.CreateRotationY(angle), RenderTypes.Shaded);
                GLHelper.UnsetGlContext();
            };

            Animation spinAnimation = new Animation()
            {
                DrawTarget      = widget,
                FramesPerSecond = 20
            };

            spinAnimation.Update += (s, time) =>
            {
                if (this.SpinLogo)
                {
                    angle += anglePerDraw;
                }
            };
            spinAnimation.Start();
        }
예제 #11
0
        public void GetRotation(ref Vector3 pOutVector)
        {
            Vector3 UpVector      = new Vector3(0, 1, 0);;
            Vector3 ForwardVector = new Vector3(0, 0, 1);

            UpVector      = Vector3.Transform(UpVector, AxisToWorld);
            ForwardVector = Vector3.Transform(ForwardVector, AxisToWorld);

            pOutVector.z = Math.Atan2(UpVector.x, UpVector.y);
            //	DebugStream("It looks like z is now " << RadToDeg(pOutVector->z));

            Matrix4X4 ZMatrix = Matrix4X4.CreateRotationZ(-pOutVector.z);

            UpVector      = Vector3.Transform(UpVector, ZMatrix);
            ForwardVector = Vector3.Transform(ForwardVector, ZMatrix);

            //	DebugStream("It looks like z is now " << RadToDeg(atan2(UpVector.x, UpVector.y)));

            pOutVector.y = -Math.Atan2(ForwardVector.x, ForwardVector.z);
            //	DebugStream("It looks like y is now " << RadToDeg(pOutVector->y));

            Matrix4X4 YMatrix = Matrix4X4.CreateRotationY(-pOutVector.y);

            UpVector      = Vector3.Transform(UpVector, YMatrix);
            ForwardVector = Vector3.Transform(ForwardVector, YMatrix);

            //	DebugStream("It looks like y is now " << RadToDeg(-atan2(ForwardVector.x, ForwardVector.z)));

            pOutVector.x = -Math.Atan2(UpVector.z, UpVector.y);
            //	DebugStream("It looks like x is now " << RadToDeg(pOutVector->x));

            //CMatrix XMatrix;
            //XMatrix.Rotate(0, -pOutVector->x, ROTATE_FAST);
            //XMatrix.TransformVector(&UpVector);
            //XMatrix.TransformVector(&ForwardVector);

            //	DebugStream("It looks like x is now " << RadToDeg(-atan2(UpVector.z, UpVector.y)));

#if _DEBUG
            CMatrix   Test;
            CVector3D TestV, HoldV;
            TestV.Set((double)(rand() % 1024), (double)(rand() % 1024), (double)(rand() % 1024));
            HoldV = TestV;
            Test.PrepareMatrix(0.f, 0.f, 0.f,
                               pOutVector->x, pOutVector->y, pOutVector->z);
            WorldToAxis.TransformVector(&TestV);
            Test.TransformVector(&TestV);
#endif
        }
예제 #12
0
        public void Vector3TransformByQuaternionTest()
        {
            Vector3D <float> v = new Vector3D <float>(1.0f, 2.0f, 3.0f);

            Matrix4X4 <float> m =
                Matrix4X4.CreateRotationX <float>(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationY <float>(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationZ <float>(MathHelper.ToRadians(30.0f));
            Quaternion <float> q = Quaternion <float> .CreateFromRotationMatrix(m);

            Vector3D <float> expected = Vector3D.Transform(v, m);
            Vector3D <float> actual   = Vector3D.Transform(v, q);

            Assert.True(MathHelper.Equal(expected, actual), "Vector3D<float>f.Transform did not return the expected value.");
        }
예제 #13
0
        public override void SetPosition(IObject3D selectedItem)
        {
            Vector3 boxCenter = GetControlCenter(selectedItem);
            double  distBetweenPixelsWorldSpace = InteractionContext.World.GetWorldUnitsPerScreenPixelAtPosition(boxCenter);

            GetCornerPosition(selectedItem, out int cornerIndexOut);

            Matrix4X4 centerMatrix = Matrix4X4.Identity;

            switch (RotationAxis)
            {
            case 0:
                if (cornerIndexOut == 1 || cornerIndexOut == 3)
                {
                    centerMatrix *= Matrix4X4.CreateRotationX(MathHelper.DegreesToRadians(90));
                }
                else
                {
                    centerMatrix *= Matrix4X4.CreateRotationY(MathHelper.DegreesToRadians(-90));
                }

                centerMatrix *= Matrix4X4.CreateRotationZ(MathHelper.DegreesToRadians(90) * cornerIndexOut);
                break;

            case 1:
                if (cornerIndexOut == 1 || cornerIndexOut == 3)
                {
                    centerMatrix *= Matrix4X4.CreateRotationY(MathHelper.DegreesToRadians(-90));
                }
                else
                {
                    centerMatrix *= Matrix4X4.CreateRotationX(MathHelper.DegreesToRadians(90));
                }

                centerMatrix *= Matrix4X4.CreateRotationZ(MathHelper.DegreesToRadians(90) * cornerIndexOut);
                break;

            case 2:
                centerMatrix *= Matrix4X4.CreateRotationZ(MathHelper.DegreesToRadians(90) * cornerIndexOut);
                break;
            }

            centerMatrix  *= Matrix4X4.CreateScale(distBetweenPixelsWorldSpace) * Matrix4X4.CreateTranslation(boxCenter);
            TotalTransform = centerMatrix;
        }
예제 #14
0
        public void Vector2TransformNormalTest()
        {
            Vector2D <float>  v = new Vector2D <float>(1.0f, 2.0f);
            Matrix4X4 <float> m =
                Matrix4X4.CreateRotationX <float>(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationY <float>(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationZ <float>(MathHelper.ToRadians(30.0f));

            m.M41 = 10.0f;
            m.M42 = 20.0f;
            m.M43 = 30.0f;

            Vector2D <float> expected = new Vector2D <float>(0.3169873f, 2.18301272f);
            Vector2D <float> actual;

            actual = Vector2D.TransformNormal(v, m);
            Assert.True(MathHelper.Equal(expected, actual), "Vector2f.Tranform did not return the expected value.");
        }
예제 #15
0
        public void Vector3TransformNormalTest()
        {
            Vector3D <float>  v = new Vector3D <float>(1.0f, 2.0f, 3.0f);
            Matrix4X4 <float> m =
                Matrix4X4.CreateRotationX <float>(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationY <float>(MathHelper.ToRadians(30.0f)) *
                Matrix4X4.CreateRotationZ <float>(MathHelper.ToRadians(30.0f));

            m.M41 = 10.0f;
            m.M42 = 20.0f;
            m.M43 = 30.0f;

            Vector3D <float> expected = new Vector3D <float>(2.19198728f, 1.53349364f, 2.61602545f);
            Vector3D <float> actual;

            actual = Vector3D.TransformNormal(v, m);
            Assert.True(MathHelper.Equal(expected, actual), "Vector3D<float>f.TransformNormal did not return the expected value.");
        }
예제 #16
0
        public void QuaternionFromRotationMatrixWithScaledMatrixTest3()
        {
            float             angle  = MathHelper.ToRadians(180.0f);
            Matrix4X4 <float> matrix = Matrix4X4.CreateRotationX <float>(angle) * Matrix4X4.CreateRotationY <float>(angle);

            Quaternion <float> expected = Quaternion <float> .CreateFromAxisAngle(Vector3D <float> .UnitY, angle) * Quaternion <float> .CreateFromAxisAngle(Vector3D <float> .UnitX, angle);

            Quaternion <float> actual = Quaternion <float> .CreateFromRotationMatrix(matrix);

            Assert.True(MathHelper.EqualRotation(expected, actual),
                        $"Quaternion<float>.CreateFromRotationMatrix did not return the expected value: expected {expected} actual {actual}");

            // make sure convert back to matrix is same as we passed matrix.
            Matrix4X4 <float> m2 = Matrix4X4.CreateFromQuaternion <float>(actual);

            Assert.True(MathHelper.Equal(matrix, m2),
                        $"Quaternion<float>.CreateFromQuaternion did not return the expected value: matrix {matrix} m2 {m2}");
        }
예제 #17
0
        public void QuaternionFromRotationMatrixTest3()
        {
            for (float angle = 0.0f; angle < 720.0f; angle += 10.0f)
            {
                Matrix4X4 <float> matrix = Matrix4X4.CreateRotationY <float>(angle);

                Quaternion <float> expected = Quaternion <float> .CreateFromAxisAngle(Vector3D <float> .UnitY, angle);

                Quaternion <float> actual = Quaternion <float> .CreateFromRotationMatrix(matrix);

                Assert.True(MathHelper.EqualRotation(expected, actual),
                            $"Quaternion<float>.CreateFromRotationMatrix angle:{angle} did not return the expected value: expected {expected} actual {actual}");

                // make sure convert back to matrix is same as we passed matrix.
                Matrix4X4 <float> m2 = Matrix4X4.CreateFromQuaternion <float>(actual);
                Assert.True(MathHelper.Equal(matrix, m2),
                            $"Quaternion<float>.CreateFromQuaternion angle:{angle} did not return the expected value: matrix {matrix} m2 {m2}");
            }
        }
예제 #18
0
        public static async Task <CylinderObject3D> Create(double diameterBottom, double diameterTop, double height, int sides, Alignment alignment = Alignment.Z)
        {
            var item = new CylinderObject3D()
            {
                Advanced    = true,
                Diameter    = diameterBottom,
                DiameterTop = diameterTop,
                Height      = height,
                Sides       = sides,
            };

            await item.Rebuild();

            switch (alignment)
            {
            case Alignment.X:
                item.Matrix = Matrix4X4.CreateRotationY(MathHelper.Tau / 4);
                break;

            case Alignment.Y:
                item.Matrix = Matrix4X4.CreateRotationX(MathHelper.Tau / 4);
                break;

            case Alignment.Z:
                // This is the natural case (how it was modeled)
                break;

            case Alignment.negX:
                item.Matrix = Matrix4X4.CreateRotationY(-MathHelper.Tau / 4);
                break;

            case Alignment.negY:
                item.Matrix = Matrix4X4.CreateRotationX(-MathHelper.Tau / 4);
                break;

            case Alignment.negZ:
                item.Matrix = Matrix4X4.CreateRotationX(MathHelper.Tau / 2);
                break;
            }

            return(item);
        }
예제 #19
0
        public void MatrixColumnMajor()
        {
            // Make sure our matrix is set up colum major like opengl. LBB [7/11/2003]
            Matrix4X4 ColumnMajorRotationMatrix    = Matrix4X4.CreateRotationY(.2345f);
            Matrix4X4 ColumnMajorTransLationMatrix = Matrix4X4.Identity;

            ColumnMajorTransLationMatrix.Translate(.2342f, 234234.734f, 223.324f);
            Matrix4X4 ColumnMajorAccumulatedMatrix = Matrix4X4.Identity;

            ColumnMajorAccumulatedMatrix = ColumnMajorRotationMatrix * ColumnMajorTransLationMatrix;
            double[] KnownMatrixFormFloats =
            {
                .972631f,       0.0f, -.232357f, 0.0f,
                0.0f,           1.0f,      0.0f, 0.0f,
                .232357f,       0.0f,  .972631f, 0.0f,
                .2342f,   234234.73f,  223.324f, 1.0f
            };
            Matrix4X4 KnownMatrixForm = Matrix4X4.Identity;

            KnownMatrixForm.SetElements(KnownMatrixFormFloats);
            Assert.IsTrue(KnownMatrixForm.Equals(ColumnMajorAccumulatedMatrix, .01f));
        }
예제 #20
0
        public override Task Rebuild()
        {
            this.DebugDepth("Rebuild");

            using (RebuildLock())
            {
                using (new CenterAndHeightMantainer(this))
                {
                    var startingAabb = this.GetAxisAlignedBoundingBox();
                    // remove whatever rotation has been applied (they go in reverse order)
                    Matrix = Matrix4X4.Identity;

                    // add the current rotation
                    Matrix = this.ApplyAtPosition(startingAabb.Center, Matrix4X4.CreateRotationX(MathHelper.DegreesToRadians(RotationXDegrees)));
                    Matrix = this.ApplyAtPosition(startingAabb.Center, Matrix4X4.CreateRotationY(MathHelper.DegreesToRadians(RotationYDegrees)));
                    Matrix = this.ApplyAtPosition(startingAabb.Center, Matrix4X4.CreateRotationZ(MathHelper.DegreesToRadians(RotationZDegrees)));
                }
            }

            Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Matrix));

            return(Task.CompletedTask);
        }
예제 #21
0
        private static World CreateWorld()
        {
            var floor = new Plane
            {
                Material = new Material
                {
                    Pattern  = new CheckersPattern(Color.White, Color.Black),
                    Color    = Color.White,
                    Specular = 0,
                }
            };

            var backWall = new Plane
            {
                TransformMatrix = new Transform().RotateX(Math.PI / 2)
                                  .RotateY(-Math.PI / 2)
                                  .Translate(1.5, 0, 0)
                                  .GetTransformationMatrix(),

                Material = new Material
                {
                    Pattern = new StripePattern(new Color(0, 0.5, 0.5), new Color(1, 0, 0))
                    {
                        TransformMatrix = Matrix4X4.CreateRotationY(Math.PI / 4)
                    },
                    Specular = 0,
                }
            };

            var middleSphere = new Sphere(Matrix4X4.CreateTranslation(-0.5, 1, 0.5))
            {
                Material = new Material
                {
                    Pattern = new RingPattern(new Color(1, 0, 0), new Color(0, 1, 0))
                    {
                        TransformMatrix = Matrix4X4.CreateRotationX(Math.PI / -3) *
                                          Matrix4X4.CreateScale(0.25, 0.25, 0.25)
                    },
                    Diffuse  = 0.7,
                    Specular = 0.3,
                }
            };

            var rightSphere = new Sphere(Matrix4X4.CreateTranslation(1.5, 0.5, -0.5) *
                                         Matrix4X4.CreateScale(0.5, 0.5, 0.5))
            {
                Material = new Material
                {
                    Color    = new Color(0.5, 1, 0.1),
                    Diffuse  = 0.7,
                    Specular = 0.3,
                }
            };

            var leftSphere = new Sphere(Matrix4X4.CreateTranslation(-1.5, 0.33, -0.75) *
                                        Matrix4X4.CreateScale(0.33, 0.33, 0.33))
            {
                Material = new Material
                {
                    Pattern = new StripePattern(new Color(0, 0, 1), new Color(0, 1, 0))
                    {
                        TransformMatrix = Matrix4X4.CreateScale(0.25, 0.25, 0.25)
                    },
                    Diffuse  = 0.7,
                    Specular = 0.3,
                }
            };

            var light = new PointLight(new Point(-10, 10, -10), new Color(1, 1, 1));

            return(new World
            {
                Objects = { middleSphere, rightSphere, leftSphere, floor, backWall },
                PointLights = { light },
            });
        }
예제 #22
0
        public void RotateRelative(double x, double y, double z)
        {
#if true
            if (x != 0)
            {
                Matrix4X4 M1 = Matrix4X4.Identity;
                M1 = Matrix4X4.CreateRotationX(x);
                if (y != 0)
                {
                    Matrix4X4 M2 = Matrix4X4.Identity;
                    Matrix4X4 M4 = Matrix4X4.Identity;
                    M2 = Matrix4X4.CreateRotationY(y);
                    M4 = M2 * M1;
                    if (z != 0)
                    {
                        Matrix4X4 M3 = Matrix4X4.Identity;
                        M3 = Matrix4X4.CreateRotationZ(z);
                        Matrix4X4 Delta = Matrix4X4.Identity;
                        Delta        = M3 * M4;
                        AxisToWorld *= Delta;
                    }
                    else
                    {
                        AxisToWorld *= M4;
                    }
                }
                else
                {
                    if (z != 0)
                    {
                        Matrix4X4 M3 = Matrix4X4.Identity;
                        M3 = Matrix4X4.CreateRotationZ(z);
                        Matrix4X4 Delta = Matrix4X4.Identity;
                        Delta        = M3 * M1;
                        AxisToWorld *= Delta;
                    }
                    else
                    {
                        AxisToWorld *= M1;
                    }
                }
            }
            else
            {
                if (y != 0)
                {
                    Matrix4X4 M2 = Matrix4X4.Identity;
                    M2 = Matrix4X4.CreateRotationY(y);
                    if (z != 0)
                    {
                        Matrix4X4 M3 = Matrix4X4.Identity;
                        M3 = Matrix4X4.CreateRotationZ(z);
                        Matrix4X4 Delta = Matrix4X4.Identity;
                        Delta        = M3 * M2;
                        AxisToWorld *= Delta;
                    }
                    else
                    {
                        AxisToWorld *= M2;
                    }
                }
                else
                {
                    if (z != 0)
                    {
                        Matrix4X4 M3 = Matrix4X4.Identity;
                        M3           = Matrix4X4.CreateRotationZ(z);
                        AxisToWorld *= M3;
                    }
                }
            }

            WorldToAxis = Matrix4X4.Invert(AxisToWorld);
#else
            M1.Rotate(0, x);
            M2.Rotate(1, y);
            M3.Rotate(2, z);
            // 1 * 2 * 3
            M4    = M2 * M1;
            Delta = M3 * M4;

            AxisToWorld = AxisToWorld * Delta;

            WorldToAxis = AxisToWorld.GetInverse();
#endif
        }
예제 #23
0
        private static World CreateWorld()
        {
            var floor = new Sphere(Matrix4X4.CreateScale(10, 0.01, 10))
            {
                Material = new Material
                {
                    Color = new Color(1, 0.9, 0.9),
                    Specular = 0,
                }
            };

            var leftWall = new Sphere(Matrix4X4.CreateTranslation(0, 0, 5) *
                                      Matrix4X4.CreateRotationY(-Math.PI / 4) *
                                      Matrix4X4.CreateRotationX(Math.PI / 2) *
                                      Matrix4X4.CreateScale(10, 0.01, 10))
            {
                Material = new Material
                {
                    Color = new Color(1, 0.9, 0.9),
                    Specular = 0,
                }
            };

            var rightWall = new Sphere(Matrix4X4.CreateTranslation(0, 0, 5) *
                                       Matrix4X4.CreateRotationY(Math.PI / 4) *
                                       Matrix4X4.CreateRotationX(Math.PI / 2) *
                                       Matrix4X4.CreateScale(10, 0.01, 10))
            {
                Material = new Material
                {
                    Color = new Color(1, 0.9, 0.9),
                    Specular = 0,
                }
            };

            var middleSphere = new Sphere(Matrix4X4.CreateTranslation(-0.5, 1, 0.5))
            {
                Material = new Material
                {
                    Color = new Color(0.1, 1, 0.5),
                    Diffuse = 0.7,
                    Specular = 0.3,
                }
            };

            var rightSphere = new Sphere(Matrix4X4.CreateTranslation(1.5, 0.5, -0.5) *
                                         Matrix4X4.CreateScale(0.5, 0.5, 0.5))
            {
                Material = new Material
                {
                    Color = new Color(0.5, 1, 0.1),
                    Diffuse = 0.7,
                    Specular = 0.3,
                }
            };

            var leftSphere = new Sphere(Matrix4X4.CreateTranslation(-1.5, 0.33, -0.75) *
                                        Matrix4X4.CreateScale(0.33, 0.33, 0.33))
            {
                Material = new Material
                {
                    Color = new Color(1, 0.8, 0.1),
                    Diffuse = 0.7,
                    Specular = 0.3,
                }
            };
            
            var light = new PointLight(new Point(-10, 10, -10), new Color(1, 1, 1));

            return new World
            {
                Objects = {floor, rightWall, leftWall, middleSphere, rightSphere, leftSphere},
                PointLights = {light},
            };
        }
        public void FrustumTransformTests()
        {
            {
                Frustum frustum = new Frustum(
                    new Plane(new Vector3(1, 0, 0), -20),
                    new Plane(new Vector3(-1, 0, 0), -20),
                    new Plane(new Vector3(0, 1, 0), -20),
                    new Plane(new Vector3(0, -1, 0), -20),
                    new Plane(new Vector3(0, 0, 1), -20),
                    new Plane(new Vector3(0, 0, -1), -20));

                Vector3Tests.TestFrustumClipLine(frustum, new Vector3(-5, 0, 0), new Vector3(5, 0, 0), true, new Vector3(-5, 0, 0), new Vector3(5, 0, 0));
                Vector3Tests.TestFrustumClipLine(frustum, new Vector3(-50, 0, 0), new Vector3(-21, 0, 0), false, null, null);
                Vector3Tests.TestFrustumClipLine(frustum, new Vector3(-50, 0, 0), new Vector3(-19, 0, 0), true, new Vector3(-20, 0, 0), new Vector3(-19, 0, 0));

                // moved right
                {
                    Frustum movedRightFrustum = Frustum.Transform(frustum, Matrix4X4.CreateTranslation(10, 0, 0));
                    Assert.IsTrue(movedRightFrustum.Planes[0] == new Plane(new Vector3(1, 0, 0), -10));
                    Assert.IsTrue(movedRightFrustum.Planes[1] == new Plane(new Vector3(-1, 0, 0), -30));
                    Assert.IsTrue(movedRightFrustum.Planes[2] == frustum.Planes[2]);
                    Assert.IsTrue(movedRightFrustum.Planes[3] == frustum.Planes[3]);
                    Assert.IsTrue(movedRightFrustum.Planes[4] == frustum.Planes[4]);
                    Assert.IsTrue(movedRightFrustum.Planes[5] == frustum.Planes[5]);

                    Vector3Tests.TestFrustumClipLine(movedRightFrustum, new Vector3(-5, 0, 0), new Vector3(5, 0, 0), true, new Vector3(-5, 0, 0), new Vector3(5, 0, 0));
                    Vector3Tests.TestFrustumClipLine(movedRightFrustum, new Vector3(-50, 0, 0), new Vector3(-11, 0, 0), false, null, null);
                    Vector3Tests.TestFrustumClipLine(movedRightFrustum, new Vector3(-50, 0, 0), new Vector3(-9, 0, 0), true, new Vector3(-10, 0, 0), new Vector3(-9, 0, 0));
                }

                // rotated right
                {
                    Frustum   movedRightFrustum = Frustum.Transform(frustum, Matrix4X4.CreateRotationY(MathHelper.DegreesToRadians(45)));
                    Matrix4X4 testMatrix        = Matrix4X4.CreateRotationY(MathHelper.DegreesToRadians(45));
                    Plane     control           = new Plane(Vector3Ex.TransformNormal(frustum.Planes[0].Normal, testMatrix), frustum.Planes[0].DistanceFromOrigin);
                    Assert.IsTrue(movedRightFrustum.Planes[0].Equals(control, .001, .01));
                    Assert.IsTrue(movedRightFrustum.Planes[1].Equals(new Plane(Vector3Ex.TransformNormal(frustum.Planes[1].Normal, testMatrix), frustum.Planes[1].DistanceFromOrigin)));
                    Assert.IsTrue(movedRightFrustum.Planes[2].Equals(frustum.Planes[2]));
                    Assert.IsTrue(movedRightFrustum.Planes[3].Equals(frustum.Planes[3]));
                    Assert.IsTrue(movedRightFrustum.Planes[4].Equals(new Plane(Vector3Ex.TransformNormal(frustum.Planes[4].Normal, testMatrix), frustum.Planes[4].DistanceFromOrigin)));
                    Assert.IsTrue(movedRightFrustum.Planes[5].Equals(new Plane(Vector3Ex.TransformNormal(frustum.Planes[5].Normal, testMatrix), frustum.Planes[5].DistanceFromOrigin)));
                }
            }

            // rotate about y 180 degrees
            {
                Matrix4X4 perspectiveMatrix  = Matrix4X4.CreatePerspectiveFieldOfView(MathHelper.Tau / 4, 1, 3, 507);
                Frustum   perspectiveFrustum = Frustum.FrustumFromProjectionMatrix(perspectiveMatrix);

                Vector3Tests.TestFrustumClipLine(perspectiveFrustum, new Vector3(-10, 0, -5), new Vector3(0, 0, -5), true, new Vector3(-5, 0, -5), new Vector3(0, 0, -5));
                Vector3Tests.TestFrustumClipLine(perspectiveFrustum, new Vector3(-50, 0, -5), new Vector3(-21, 0, -5), false, null, null);
                Vector3Tests.TestFrustumClipLine(perspectiveFrustum, new Vector3(-50, 0, -20), new Vector3(-19, 0, -20), true, new Vector3(-20, 0, -20), new Vector3(-19, 0, -20));

                var frustum = Frustum.Transform(perspectiveFrustum, Matrix4X4.CreateRotationY(MathHelper.Tau / 2));

                // left
                Assert.IsTrue(frustum.Planes[0].Normal.Equals(new Vector3(-1, 0, 1).GetNormal(), .0001));
                Assert.AreEqual(frustum.Planes[0].DistanceFromOrigin, 0, .0001);
                // right
                Assert.IsTrue(frustum.Planes[1].Normal.Equals(new Vector3(1, 0, 1).GetNormal(), .0001));
                Assert.AreEqual(frustum.Planes[1].DistanceFromOrigin, 0, .0001);
                // bottom
                Assert.IsTrue(frustum.Planes[2].Normal.Equals(new Vector3(0, 1, 1).GetNormal(), .0001));
                Assert.AreEqual(frustum.Planes[2].DistanceFromOrigin, 0, .0001);
                // top
                Assert.IsTrue(frustum.Planes[3].Normal.Equals(new Vector3(0, -1, 1).GetNormal(), .0001));
                Assert.AreEqual(frustum.Planes[3].DistanceFromOrigin, 0, .0001);
                // near
                Assert.IsTrue(frustum.Planes[4].Normal.Equals(new Vector3(0, 0, 1), .0001));
                Assert.AreEqual(frustum.Planes[4].DistanceFromOrigin, 3, .0001);
                // far
                Assert.IsTrue(frustum.Planes[5].Normal.Equals(new Vector3(0, 0, -1), .0001));
                Assert.AreEqual(frustum.Planes[5].DistanceFromOrigin, -507, .0001);
            }

            // translate 10 down z
            {
                double    zMove             = 10;
                Matrix4X4 perspectiveMatrix = Matrix4X4.CreatePerspectiveFieldOfView(MathHelper.Tau / 4, 1, 3, 507);
                Frustum   frustum           = Frustum.FrustumFromProjectionMatrix(perspectiveMatrix);
                frustum = Frustum.Transform(frustum, Matrix4X4.CreateTranslation(0, 0, -10));

                double expectedPlaneOffset = Math.Sqrt(2 * (zMove / 2) * (zMove / 2));
                // left
                Assert.IsTrue(frustum.Planes[0].Normal.Equals(new Vector3(1, 0, -1).GetNormal(), .0001));
                Assert.AreEqual(expectedPlaneOffset, frustum.Planes[0].DistanceFromOrigin, .0001);
                // right
                Assert.IsTrue(frustum.Planes[1].Normal.Equals(new Vector3(-1, 0, -1).GetNormal(), .0001));
                Assert.AreEqual(expectedPlaneOffset, frustum.Planes[1].DistanceFromOrigin, .0001);
                // bottom
                Assert.IsTrue(frustum.Planes[2].Normal.Equals(new Vector3(0, 1, -1).GetNormal(), .0001));
                Assert.AreEqual(expectedPlaneOffset, frustum.Planes[2].DistanceFromOrigin, .0001);
                // top
                Assert.IsTrue(frustum.Planes[3].Normal.Equals(new Vector3(0, -1, -1).GetNormal(), .0001));
                Assert.AreEqual(expectedPlaneOffset, frustum.Planes[3].DistanceFromOrigin, .0001);
                // near
                Assert.IsTrue(frustum.Planes[4].Normal.Equals(new Vector3(0, 0, -1), .0001));
                Assert.AreEqual(frustum.Planes[4].DistanceFromOrigin, 3 + zMove, .0001);
                // far
                Assert.IsTrue(frustum.Planes[5].Normal.Equals(new Vector3(0, 0, 1), .0001));
                Assert.AreEqual(frustum.Planes[5].DistanceFromOrigin, -507 - zMove, .0001);
            }
        }
예제 #25
0
        // Main display routine
        public void glutDisplayFunc(WorldView worldView, IObject3D item)
        {
            var mesh    = item.Mesh;
            var meshVao = GLMeshVertexArrayObjectPlugin.Get(mesh);

            GL.PushAttrib(AttribMask.EnableBit | AttribMask.ViewportBit | AttribMask.TransformBit);

            // Projection and modelview matrices
            float near  = 0.01f;
            float far   = 20;
            var   top   = Math.Tan(35.0 / 360.0 * Math.PI) * near;
            var   right = top * w / h;
            var   proj  = Matrix4X4.Frustum(-right, right, -top, top, near, far);
            // spin around
            var model = Matrix4X4.CreateRotationY(Math.PI / 180.0 * count++) * Matrix4X4.CreateTranslation(0, 0, -1.5);

            //proj = worldView.ProjectionMatrix;
            //model = worldView.ModelviewMatrix;

            gl.Enable(GL.DEPTH_TEST);
            gl.Viewport(0, 0, w, h);
            // select program and attach uniforms
            gl.UseProgram(scene_p_id);
            int proj_loc = gl.GetUniformLocation(scene_p_id, "proj");

            gl.UniformMatrix4fv(proj_loc, 1, GL.FALSE, proj.GetAsFloatArray());
            int model_loc = gl.GetUniformLocation(scene_p_id, "model");

            gl.UniformMatrix4fv(model_loc, 1, GL.FALSE, model.GetAsFloatArray());
            gl.Uniform1f(gl.GetUniformLocation(scene_p_id, "width"), w);
            gl.Uniform1f(gl.GetUniformLocation(scene_p_id, "height"), h);
            gl.BindVertexArray(meshVao.Vao);
            gl.Disable(GL.BLEND);
            for (int pass = 0; pass < renderPasses; pass++)
            {
                int first_pass = pass == 0 ? 1 : 0;
                gl.Uniform1i(gl.GetUniformLocation(scene_p_id, "first_pass"), first_pass);
                if (first_pass == 0)
                {
                    gl.Uniform1i(gl.GetUniformLocation(scene_p_id, "depth_texture"), 0);
                    gl.ActiveTexture(GL.TEXTURE0 + 0);
                    GL.BindTexture(TextureTarget.Texture2D, depthTexture[pass - 1]);
                }
                gl.BindFramebuffer(GL.FRAMEBUFFER, frameBufferObject[pass]);
                gl.ClearColor(0.0, 0.4, 0.7, 0.0);
                gl.Clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
                gl.DrawElements(GL.TRIANGLES, mesh.Faces.Count * 3, GL.UNSIGNED_INT, IntPtr.Zero);
            }

            // clean up and set to render to screen
            gl.BindVertexArray(0);
            gl.BindFramebuffer(GL.FRAMEBUFFER, 0);
            gl.ActiveTexture(GL.TEXTURE0 + 0);
            GL.BindTexture(TextureTarget.Texture2D, 0);

            gl.ClearColor(0.0, 0.4, 0.7, 0.0);
            gl.Clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);

            for (int pass = 0; pass < renderPasses; pass++)
            {
                RenderLayer(pass);
            }

            RenderFinal();

            gl.DepthFunc(GL.LESS);

            GL.PopAttrib();
            gl.BindVertexArray(0);
            GL.BindTexture(TextureTarget.Texture2D, 0);
            gl.UseProgram(0);
        }
예제 #26
0
        private static void AddRevolveStrip(IVertexSource vertexSource, Mesh mesh, double startAngle, double endAngle, bool revolveAroundZ)
        {
            Vector3 lastPosition  = Vector3.Zero;
            Vector3 firstPosition = Vector3.Zero;

            foreach (var vertexData in vertexSource.Vertices())
            {
                if (vertexData.IsStop)
                {
                    break;
                }

                if (vertexData.IsMoveTo)
                {
                    firstPosition = new Vector3(vertexData.position.X, 0, vertexData.position.Y);
                    if (!revolveAroundZ)
                    {
                        firstPosition = new Vector3(vertexData.position.X, vertexData.position.Y, 0);
                    }

                    lastPosition = firstPosition;
                }

                if (vertexData.IsLineTo || vertexData.IsClose)
                {
                    var currentPosition = new Vector3(vertexData.position.X, 0, vertexData.position.Y);
                    if (!revolveAroundZ)
                    {
                        currentPosition = new Vector3(vertexData.position.X, vertexData.position.Y, 0);
                    }

                    if (vertexData.IsClose)
                    {
                        currentPosition = firstPosition;
                    }

                    if (currentPosition.X != 0 || lastPosition.X != 0)
                    {
                        if (revolveAroundZ)
                        {
                            mesh.CreateFace(new Vector3[]
                            {
                                Vector3Ex.Transform(currentPosition, Matrix4X4.CreateRotationZ(endAngle)),
                                Vector3Ex.Transform(currentPosition, Matrix4X4.CreateRotationZ(startAngle)),
                                Vector3Ex.Transform(lastPosition, Matrix4X4.CreateRotationZ(startAngle)),
                                Vector3Ex.Transform(lastPosition, Matrix4X4.CreateRotationZ(endAngle)),
                            });
                        }
                        else
                        {
                            mesh.CreateFace(new Vector3[]
                            {
                                Vector3Ex.Transform(currentPosition, Matrix4X4.CreateRotationY(endAngle)),
                                Vector3Ex.Transform(currentPosition, Matrix4X4.CreateRotationY(startAngle)),
                                Vector3Ex.Transform(lastPosition, Matrix4X4.CreateRotationY(startAngle)),
                                Vector3Ex.Transform(lastPosition, Matrix4X4.CreateRotationY(endAngle)),
                            });
                        }
                    }

                    lastPosition = currentPosition;
                }
            }
        }
예제 #27
0
        public static Mesh Revolve(this IVertexSource source,
                                   int angleSteps      = 30,
                                   double angleStart   = 0,
                                   double angleEnd     = MathHelper.Tau,
                                   bool revolveAroundZ = true)
        {
            angleStart = MathHelper.Range0ToTau(angleStart);
            angleEnd   = MathHelper.Range0ToTau(angleEnd);

            // make sure we close 360 shapes
            angleStart = fixCloseAngles(angleStart);
            angleEnd   = fixCloseAngles(angleEnd);

            if (angleStart == 0 && angleEnd == MathHelper.Tau)
            {
                angleSteps = Math.Max(angleSteps, 3);
            }
            else
            {
                angleSteps = Math.Max(angleSteps, 1);
            }

            // convert to clipper polygons and scale so we can ensure good shapes
            Polygons polygons = source.CreatePolygons();

            if (polygons.Select(poly => poly.Where(pos => pos.X < 0)).Any())
            {
                // ensure good winding and consistent shapes
                polygons = polygons.GetCorrectedWinding();
                var bounds = polygons.GetBounds();
                bounds.Inflate(10);
                // clip against x=0 left and right
                var leftClip = new Polygon();
                leftClip.Add(new IntPoint(0, bounds.Bottom));
                leftClip.Add(new IntPoint(0, bounds.Top));
                leftClip.Add(new IntPoint(bounds.Left, bounds.Top));
                leftClip.Add(new IntPoint(bounds.Left, bounds.Bottom));
                var rightStuff = polygons.Subtract(leftClip);

                var rightClip = new Polygon();
                rightClip.Add(new IntPoint(0, bounds.Top));
                rightClip.Add(new IntPoint(0, bounds.Bottom));
                rightClip.Add(new IntPoint(bounds.Right, bounds.Bottom));
                rightClip.Add(new IntPoint(bounds.Right, bounds.Top));
                var leftStuff = polygons.Subtract(rightClip);
                // mirror left material across the origin
                var leftAdd = leftStuff.Scale(-1, 1);
                if (leftAdd.Count > 0)
                {
                    if (rightStuff.Count > 0)
                    {
                        polygons = rightStuff.Union(leftAdd);
                    }
                    else
                    {
                        polygons = leftAdd;
                    }
                }
                else
                {
                    // there is nothing on the left
                    polygons = rightStuff;
                }
            }

            // convert the data back to PathStorage
            VertexStorage cleanedPath = polygons.CreateVertexStorage();

            var mesh = new Mesh();

            var hasStartAndEndFaces = angleStart > 0.000001;

            hasStartAndEndFaces |= angleEnd < MathHelper.Tau - 0.000001;
            // check if we need to make closing faces
            if (hasStartAndEndFaces)
            {
                // make a face for the start
                Mesh extrudedVertexSource = cleanedPath.TriangulateFaces();
                if (revolveAroundZ)
                {
                    extrudedVertexSource.Transform(Matrix4X4.CreateRotationX(MathHelper.Tau / 4));
                    extrudedVertexSource.Transform(Matrix4X4.CreateRotationZ(angleStart));
                }
                else
                {
                    extrudedVertexSource.Transform(Matrix4X4.CreateRotationY(angleStart));
                }

                mesh.CopyFaces(extrudedVertexSource);
            }

            // make the outside shell
            double angleDelta   = (angleEnd - angleStart) / angleSteps;
            double currentAngle = angleStart;

            if (!hasStartAndEndFaces)
            {
                angleSteps--;
            }

            for (int i = 0; i < angleSteps; i++)
            {
                AddRevolveStrip(cleanedPath, mesh, currentAngle, currentAngle + angleDelta, revolveAroundZ);
                currentAngle += angleDelta;
            }

            if (!hasStartAndEndFaces)
            {
                if (((angleEnd - angleStart) < .0000001 ||
                     (angleEnd - MathHelper.Tau - angleStart) < .0000001) &&
                    (angleEnd - currentAngle) > .0000001)
                {
                    // make sure we close the shape exactly
                    AddRevolveStrip(cleanedPath, mesh, currentAngle, angleStart, revolveAroundZ);
                }
            }
            else             // add the end face
            {
                // make a face for the end
                Mesh extrudedVertexSource = cleanedPath.TriangulateFaces();
                if (revolveAroundZ)
                {
                    extrudedVertexSource.Transform(Matrix4X4.CreateRotationX(MathHelper.Tau / 4));
                    extrudedVertexSource.Transform(Matrix4X4.CreateRotationZ(currentAngle));
                }
                else
                {
                    extrudedVertexSource.Transform(Matrix4X4.CreateRotationY(angleStart));
                }

                extrudedVertexSource.ReverseFaces();
                mesh.CopyFaces(extrudedVertexSource);
            }

            mesh.CleanAndMerge();

            // return the completed mesh
            return(mesh);
        }