Example #1
0
 /// <summary>
 /// Builds a cube into this Geometry (this cube is built up of 24 vertices, so texture coordinates and normals are set)
 /// </summary>
 public static BuiltVerticesRange BuildCube(this GeometrySurface target, float width, float height, float depth)
 {
     return(BuildCube(
                target,
                new Vector3(-(width / 2f), -(height / 2f), -(depth / 2f)),
                new Vector3(width, height, depth)));
 }
Example #2
0
 /// <summary>
 /// Builds a cube into this Geometry (this cube is built up of 24 vertices, so texture coordinates and normals are set)
 /// </summary>
 public static BuiltVerticesRange BuildCube(this GeometrySurface target, Vector3 size)
 {
     return(BuildCube(
                target,
                new Vector3(-(size.X / 2f), -(size.Y / 2f), -(size.Z / 2f)),
                new Vector3(size.X, size.Y, size.Z)));
 }
Example #3
0
        /// <summary>
        /// Builds cube sides into this Geometry (these sides are built up of  16 vertices, so texture coordinates and normals are set)
        /// </summary>
        /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
        /// <param name="start">Start point of the cube</param>
        /// <param name="size">Size of the cube</param>
        public static BuiltVerticesRange BuildCubeSides(this GeometrySurface target, Vector3 start, Vector3 size)
        {
            var result = new BuiltVerticesRange(target.Owner)
            {
                StartVertex = target.Owner.CountVertices
            };

            var dest = start + size;

            var texX = 1f;
            var texY = 1f;
            var texZ = 1f;

            if (target.IsTextureTileModeEnabled(out var tileSize))
            {
                texX = size.X / tileSize.X;
                texY = size.Y / tileSize.Y;
                texZ = size.Z / tileSize.X;
            }

            //Front side
            var vertex = new VertexBasic(start, new Vector2(0f, texY), new Vector3(0f, 0f, -1f));
            var a      = target.Owner.AddVertex(vertex);
            var b      = target.Owner.AddVertex(vertex.Copy(new Vector3(dest.X, start.Y, start.Z), new Vector2(texX, texY)));
            var c      = target.Owner.AddVertex(vertex.Copy(new Vector3(dest.X, dest.Y, start.Z), new Vector2(texX, 0f)));
            var d      = target.Owner.AddVertex(vertex.Copy(new Vector3(start.X, dest.Y, start.Z), new Vector2(0f, 0f)));

            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            //Right side
            a = target.Owner.AddVertex(vertex.Copy(new Vector3(dest.X, start.Y, start.Z), new Vector3(1f, 0f, 0f), new Vector2(0f, texY)));
            b = target.Owner.AddVertex(vertex.Copy(new Vector3(dest.X, start.Y, dest.Z), new Vector3(1f, 0f, 0f), new Vector2(texZ, texY)));
            c = target.Owner.AddVertex(vertex.Copy(new Vector3(dest.X, dest.Y, dest.Z), new Vector3(1f, 0f, 0f), new Vector2(texZ, 0f)));
            d = target.Owner.AddVertex(vertex.Copy(new Vector3(dest.X, dest.Y, start.Z), new Vector3(1f, 0f, 0f), new Vector2(0f, 0f)));
            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            //Back side
            a = target.Owner.AddVertex(vertex.Copy(new Vector3(dest.X, start.Y, dest.Z), new Vector3(0f, 0f, 1f), new Vector2(0f, texY)));
            b = target.Owner.AddVertex(vertex.Copy(new Vector3(start.X, start.Y, dest.Z), new Vector3(0f, 0f, 1f), new Vector2(texX, texY)));
            c = target.Owner.AddVertex(vertex.Copy(new Vector3(start.X, dest.Y, dest.Z), new Vector3(0f, 0f, 1f), new Vector2(texX, 0f)));
            d = target.Owner.AddVertex(vertex.Copy(new Vector3(dest.X, dest.Y, dest.Z), new Vector3(0f, 0f, 1f), new Vector2(0f, 0f)));
            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            //Left side
            a = target.Owner.AddVertex(vertex.Copy(new Vector3(start.X, start.Y, dest.Z), new Vector3(-1f, 0f, 0f), new Vector2(0f, texY)));
            b = target.Owner.AddVertex(vertex.Copy(new Vector3(start.X, start.Y, start.Z), new Vector3(-1f, 0f, 0f), new Vector2(texZ, texY)));
            c = target.Owner.AddVertex(vertex.Copy(new Vector3(start.X, dest.Y, start.Z), new Vector3(-1f, 0f, 0f), new Vector2(texZ, 0f)));
            d = target.Owner.AddVertex(vertex.Copy(new Vector3(start.X, dest.Y, dest.Z), new Vector3(-1f, 0f, 0f), new Vector2(0f, 0f)));
            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            result.VertexCount = target.Owner.CountVertices - result.StartVertex;
            return(result);
        }
Example #4
0
        /// <summary>
        /// Builds a cube into this Geometry (this cube is built up of 24 vertices, so texture coordinates and normals are set)
        /// </summary>
        /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
        /// <param name="start">Start point of the cube</param>
        /// <param name="size">Size of the cube</param>
        public static BuiltVerticesRange BuildCube(this GeometrySurface target, Vector3 start, Vector3 size)
        {
            var result = new BuiltVerticesRange(target.Owner);

            result.Merge(target.BuildCubeSides(start, size));
            result.Merge(target.BuildCubeTop(start, size));
            result.Merge(target.BuildCubeBottom(start, size));

            return(result);
        }
Example #5
0
        /// <summary>
        /// Builds a cube into this Geometry (this cube is built up of 24 vertices, so texture coordinates and normals are set)
        /// </summary>
        /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
        /// <param name="bottomCenter">Bottom center point of the cube.</param>
        /// <param name="width">Width (and depth) of the cube.</param>
        /// <param name="height">Height of the cube.</param>
        public static BuiltVerticesRange BuildCube(this GeometrySurface target, Vector3 bottomCenter, float width, float height)
        {
            var start = new Vector3(
                bottomCenter.X - width / 2f,
                bottomCenter.Y,
                bottomCenter.Z - width / 2f);
            var size = new Vector3(width, height, width);

            return(target.BuildCube(start, size));
        }
Example #6
0
        /// <summary>
        /// Builds the bottom side of a cube into this Geometry (Built up of 4 vertices, so texture coordinates and normals are set)
        /// </summary>
        public static BuiltVerticesRange BuildCubeBottom(this GeometrySurface target, Vector3 start, Vector3 size)
        {
            var dest = start + size;

            return(target.BuildRect(
                       new Vector3(start.X, start.Y, dest.Z),
                       new Vector3(dest.X, start.Y, dest.Z),
                       new Vector3(dest.X, start.Y, start.Z),
                       new Vector3(start.X, start.Y, start.Z),
                       new Vector3(0f, -1f, 0f)));
        }
        /// <summary>
        /// Create a 4 Side Pyramid
        /// </summary>
        public static BuiltVerticesRange BuildPyramid(this GeometrySurface target, float width, float height)
        {
            width  = Math.Max(EngineMath.TOLERANCE_FLOAT_POSITIVE, width);
            height = Math.Max(EngineMath.TOLERANCE_FLOAT_POSITIVE, height);

            var halfWidth             = width / 2f;
            var lowerMiddle           = new Vector3(0f, -(height / 2f), 0f);
            var start                 = new Vector3(lowerMiddle.X - halfWidth, lowerMiddle.Y, lowerMiddle.Z - halfWidth);
            var dest                  = start + new Vector3(width, 0f, width);
            var centerTopCoordination = new Vector3((dest.X + start.X) / 2, start.Y + height, (dest.Z + start.Z) / 2);

            target.BuildRect(
                new Vector3(start.X, dest.Y, start.Z),
                new Vector3(start.X, dest.Y, dest.Z),
                new Vector3(dest.X, dest.Y, dest.Z),
                new Vector3(dest.X, dest.Y, start.Z),
                new Vector3(0f, -1f, 0f));

            target.BuildTriangle(
                new Vector3(start.X, dest.Y, start.Z),
                new Vector3(dest.X, dest.Y, start.Z),
                centerTopCoordination);
            target.BuildTriangle(
                new Vector3(start.X, dest.Y, dest.Z),
                new Vector3(start.X, dest.Y, start.Z),
                centerTopCoordination);
            target.BuildTriangle(
                new Vector3(dest.X, dest.Y, dest.Z),
                new Vector3(start.X, dest.Y, dest.Z),
                centerTopCoordination);
            target.BuildTriangle(
                new Vector3(dest.X, dest.Y, start.Z),
                new Vector3(dest.X, dest.Y, dest.Z),
                centerTopCoordination);

            return(new BuiltVerticesRange(target.Owner, target.Owner.CountVertices - 4, 4));
        }
        /// <summary>
        /// Builds a sphere geometry.
        /// </summary>
        public static BuiltVerticesRange BuildShpere(this GeometrySurface target, int tDiv, int pDiv, double radius)
        {
            tDiv   = Math.Max(tDiv, 3);
            pDiv   = Math.Max(pDiv, 2);
            radius = Math.Max(Math.Abs(radius), EngineMath.TOLERANCE_FLOAT_POSITIVE);

            Vector3 SphereGetPosition(double theta, double phi)
            {
                var x = radius * Math.Sin(theta) * Math.Sin(phi);
                var y = radius * Math.Cos(phi);
                var z = radius * Math.Cos(theta) * Math.Sin(phi);

                return(new Vector3((float)x, (float)y, (float)z));
            }

            Vector2 SphereGetTextureCoordinate(double theta, double phi)
            {
                return(new Vector2(
                           (float)(theta / (2 * Math.PI)),
                           (float)(phi / Math.PI)));
            }

            var startVertex = target.Owner.CountVertices;
            var dt          = Math.PI * 2 / tDiv;
            var dp          = Math.PI / pDiv;

            for (var pi = 0; pi <= pDiv; pi++)
            {
                var phi = pi * dp;

                for (var ti = 0; ti <= tDiv; ti++)
                {
                    // we want to start the mesh on the x axis
                    var theta = ti * dt;

                    var position = SphereGetPosition(theta, phi);
                    var vertex   = new VertexBasic(
                        position,
                        SphereGetTextureCoordinate(theta, phi),
                        Vector3.Normalize(position));
                    target.Owner.Vertices.Add(vertex);
                }
            }

            for (var pi = 0; pi < pDiv; pi++)
            {
                for (var ti = 0; ti < tDiv; ti++)
                {
                    var x0 = ti;
                    var x1 = ti + 1;
                    var y0 = pi * (tDiv + 1);
                    var y1 = (pi + 1) * (tDiv + 1);

                    target.Triangles.Add(
                        x0 + y0,
                        x0 + y1,
                        x1 + y0);

                    target.Triangles.Add(
                        x1 + y0,
                        x0 + y1,
                        x1 + y1);
                }
            }

            return(new BuiltVerticesRange(target.Owner, startVertex, target.Owner.CountVertices - startVertex));
        }
Example #9
0
        /// <summary>
        /// Builds a cube of 4 vertices and a defined height.
        /// </summary>
        public static BuiltVerticesRange BuildCube(this GeometrySurface target, Vector3 topA, Vector3 topB, Vector3 topC, Vector3 topD, float height)
        {
            var result = new BuiltVerticesRange(target.Owner)
            {
                StartVertex = target.Owner.CountVertices
            };

            var startTriangleIndex = target.CountTriangles;

            // Calculate texture coordinates
            var size = new Vector3(
                (topB - topA).Length(),
                Math.Abs(height),
                (topC - topB).Length());
            var texX = 1f;
            var texY = 1f;
            var texZ = 1f;

            if (target.IsTextureTileModeEnabled(out var tileSize))
            {
                texX = size.X / tileSize.X;
                texY = size.Y / tileSize.Y;
                texZ = size.Z / tileSize.X;
            }

            // Calculate bottom vectors
            var bottomA = new Vector3(topA.X, topA.Y - height, topA.Z);
            var bottomB = new Vector3(topB.X, topB.Y - height, topB.Z);
            var bottomC = new Vector3(topC.X, topC.Y - height, topC.Z);
            var bottomD = new Vector3(topD.X, topD.Y - height, topD.Z);

            // Build Top side
            var vertex = new VertexBasic(topA, new Vector2(texX, 0f), new Vector3(0f, 1f, 0f));
            var a      = target.Owner.AddVertex(vertex);
            var b      = target.Owner.AddVertex(vertex.Copy(topB, new Vector2(texX, texY)));
            var c      = target.Owner.AddVertex(vertex.Copy(topC, new Vector2(0f, texY)));
            var d      = target.Owner.AddVertex(vertex.Copy(topD, new Vector2(0f, 0f)));

            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            // Build Bottom side
            vertex = new VertexBasic(topA, new Vector2(0f, 0f), new Vector3(0f, -1f, 0f));
            a      = target.Owner.AddVertex(vertex);
            b      = target.Owner.AddVertex(vertex.Copy(topD, new Vector2(texX, 0f)));
            c      = target.Owner.AddVertex(vertex.Copy(topC, new Vector2(texX, texY)));
            d      = target.Owner.AddVertex(vertex.Copy(topB, new Vector2(0f, texY)));
            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            // Build Front side
            vertex = new VertexBasic(topA, new Vector2(0f, texY), new Vector3(0f, 0f, -1f));
            a      = target.Owner.AddVertex(vertex);
            b      = target.Owner.AddVertex(vertex.Copy(topB, new Vector2(texX, texY)));
            c      = target.Owner.AddVertex(vertex.Copy(bottomB, new Vector2(texX, 0f)));
            d      = target.Owner.AddVertex(vertex.Copy(bottomA, new Vector2(0f, 0f)));
            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            // Build Right side
            a = target.Owner.AddVertex(vertex.Copy(topB, new Vector3(1f, 0f, 0f), new Vector2(0f, texY)));
            b = target.Owner.AddVertex(vertex.Copy(topC, new Vector3(1f, 0f, 0f), new Vector2(texZ, texY)));
            c = target.Owner.AddVertex(vertex.Copy(bottomC, new Vector3(1f, 0f, 0f), new Vector2(texZ, 0f)));
            d = target.Owner.AddVertex(vertex.Copy(bottomB, new Vector3(1f, 0f, 0f), new Vector2(0f, 0f)));
            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            // Build Back side
            a = target.Owner.AddVertex(vertex.Copy(topC, new Vector3(0f, 0f, 1f), new Vector2(0f, texY)));
            b = target.Owner.AddVertex(vertex.Copy(topD, new Vector3(0f, 0f, 1f), new Vector2(texX, texY)));
            c = target.Owner.AddVertex(vertex.Copy(bottomD, new Vector3(0f, 0f, 1f), new Vector2(texX, 0f)));
            d = target.Owner.AddVertex(vertex.Copy(bottomC, new Vector3(0f, 0f, 1f), new Vector2(0f, 0f)));
            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            // Build Left side
            a = target.Owner.AddVertex(vertex.Copy(topD, new Vector3(-1f, 0f, 0f), new Vector2(0f, texY)));
            b = target.Owner.AddVertex(vertex.Copy(topA, new Vector3(-1f, 0f, 0f), new Vector2(texZ, texY)));
            c = target.Owner.AddVertex(vertex.Copy(bottomA, new Vector3(-1f, 0f, 0f), new Vector2(texZ, 0f)));
            d = target.Owner.AddVertex(vertex.Copy(bottomD, new Vector3(-1f, 0f, 0f), new Vector2(0f, 0f)));
            target.AddTriangle(a, c, b);
            target.AddTriangle(a, d, c);

            // Calculate normals finally
            target.CalculateNormalsFlat(startTriangleIndex, target.CountTriangles - startTriangleIndex);

            result.VertexCount = target.Owner.CountVertices - result.StartVertex;
            return(result);
        }
Example #10
0
 /// <summary>
 /// Builds a cube on the given point with the given color.
 /// </summary>
 /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
 /// <param name="centerLocation">The location to draw the cube at.</param>
 /// <param name="sideLength">The side length of the cube.</param>
 public static BuiltVerticesRange BuildCube(this GeometrySurface target, Vector3 centerLocation, float sideLength)
 {
     return(target.BuildCube(
                centerLocation - new Vector3(sideLength / 2f, sideLength / 2f, sideLength / 2f),
                new Vector3(sideLength, sideLength, sideLength)));
 }
Example #11
0
 /// <summary>
 /// Builds a cube into this Geometry (this cube is built up of 24 vertices, so texture coordinates and normals are set)
 /// </summary>
 /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
 /// <param name="box">Box defining bounds of generated cube.</param>
 public static BuiltVerticesRange BuildCube(this GeometrySurface target, BoundingBox box)
 {
     return(target.BuildCube(box.Minimum, box.GetSize()));
 }
Example #12
0
        /// <summary>
        /// Builds a cone into the geometry with correct texture coordinates and normals.
        /// </summary>
        /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
        /// <param name="radius">The radius of the cone.</param>
        /// <param name="height">The height of the cone.</param>
        /// <param name="countOfSegments">Total count of segments to generate.</param>
        public static BuiltVerticesRange BuildCone(this GeometrySurface target, float radius, float height, int countOfSegments)
        {
            var startVertex = target.Owner.CountVertices;

            radius          = Math.Max(EngineMath.TOLERANCE_FLOAT_POSITIVE, radius);
            height          = Math.Max(EngineMath.TOLERANCE_FLOAT_POSITIVE, height);
            countOfSegments = Math.Max(3, countOfSegments);

            var diameter = radius * 2f;

            // Get texture offsets
            var texX = 1f;
            var texY = 1f;

            if (target.IsTextureTileModeEnabled(out var tileSize))
            {
                texX = diameter / tileSize.X;
                texY = diameter / tileSize.Y;
            }

            // Specify bottom and top middle coordinates
            var bottomCoordinate = new Vector3(0f, -(height / 2f), 0f);
            var topCoordinate    = new Vector3(bottomCoordinate.X, bottomCoordinate.Y + height, bottomCoordinate.Z);

            // Create bottom and top vertices
            var bottomVertex = new VertexBasic(bottomCoordinate, new Vector2(texX / 2f, texY / 2f), new Vector3(0f, -1f, 0f));

            // AddObject bottom and top vertices to the geometry
            var bottomVertexIndex = target.Owner.AddVertex(bottomVertex);

            // Generate all segments
            var countOfSegmentsF = (float)countOfSegments;

            for (var loop = 0; loop < countOfSegments; loop++)
            {
                // Calculate rotation values for each segment border
                var startRadian  = EngineMath.RAD_360DEG * (loop / countOfSegmentsF);
                var targetRadian = EngineMath.RAD_360DEG * ((loop + 1) / countOfSegmentsF);
                var normalRadian = startRadian + (targetRadian - startRadian) / 2f;

                // Generate all normals
                var sideNormal      = Vector3Ex.NormalFromHVRotation(normalRadian, 0f);
                var sideLeftNormal  = Vector3Ex.NormalFromHVRotation(startRadian, 0f);
                var sideRightNormal = Vector3Ex.NormalFromHVRotation(targetRadian, 0f);

                //Generate all points
                var sideLeftBottomCoord   = bottomCoordinate + sideLeftNormal * radius;
                var sideRightBottomCoord  = bottomCoordinate + sideRightNormal * radius;
                var sideMiddleBottomCoord = bottomCoordinate + sideNormal * radius;
                var sideLeftTexCoord      = new Vector2(
                    texX / (diameter / (sideLeftBottomCoord.X + radius)),
                    texY / (diameter / (sideLeftBottomCoord.Z + radius)));
                var sideRightTexCoord = new Vector2(
                    texX / (diameter / (sideRightBottomCoord.X + radius)),
                    texY / (diameter / (sideRightBottomCoord.Z + radius)));

                //AddObject segment bottom triangle
                var segmentBottomLeft  = bottomVertex.Copy(sideLeftBottomCoord, sideLeftTexCoord);
                var segmentBottomRight = bottomVertex.Copy(sideRightBottomCoord, sideRightTexCoord);
                target.AddTriangle(
                    bottomVertexIndex, target.Owner.AddVertex(segmentBottomLeft), target.Owner.AddVertex(segmentBottomRight));

                //Generate side normal
                var vectorToTop         = topCoordinate - sideMiddleBottomCoord;
                var vectorToTopRotation = Vector3Ex.ToHVRotation(vectorToTop);
                vectorToTopRotation.Y = vectorToTopRotation.Y + EngineMath.RAD_90DEG;
                var topSideNormal = Vector3Ex.NormalFromHVRotation(vectorToTopRotation);

                //AddObject segment top triangle
                var topVertex       = new VertexBasic(topCoordinate, new Vector2(texX / 2f, texY / 2f), topSideNormal);
                var segmentTopLeft  = topVertex.Copy(sideLeftBottomCoord, sideLeftTexCoord);
                var segmentTopRight = topVertex.Copy(sideRightBottomCoord, sideRightTexCoord);

                target.AddTriangle(target.Owner.AddVertex(topVertex), target.Owner.AddVertex(segmentTopRight), target.Owner.AddVertex(segmentTopLeft));
            }

            return(new BuiltVerticesRange(target.Owner, startVertex, target.Owner.CountVertices - startVertex));
        }
Example #13
0
        /// <summary>
        /// Builds a torus.
        /// </summary>
        /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
        /// <param name="torusRadius">The radius of the torus.</param>
        /// <param name="tubeRadius">The radius of the torus "tube".</param>
        /// <param name="tDiv">The number of subdivisions around the torus.</param>
        /// <param name="pDiv">The number of subdivisions of the torus' "tube.</param>
        public static BuiltVerticesRange BuildTorus(
            this GeometrySurface target,
            int tDiv, int pDiv, float torusRadius, float tubeRadius)
        {
            var startVertex = target.Owner.CountVertices;

            var torusDiameter = torusRadius * 2f;
            var tubeDiameter  = tubeRadius * 2f;

            if (torusDiameter == 0.0)
            {
                // No Torus Diameter means we treat the Visual3D like a Sphere
                target.BuildShpere(tDiv, pDiv, tubeDiameter);
            }
            else if (tubeDiameter == 0.0)
            {
                // If the second Diameter is zero, we can't build out torus
                throw new SeeingSharpException("Torus must have a Diameter bigger than 0");
            }

            // Points of the Cross-Section of the torus "tube"
            IList <Vector2> crossSectionPoints;

            // Self-intersecting Torus, if the "Tube" Diameter is bigger than the Torus Diameter
            var selfIntersecting = tubeDiameter > torusDiameter;

            if (selfIntersecting)
            {
                // Angle-Calculations for Circle Segment https://de.wikipedia.org/wiki/Gleichschenkliges_Dreieck
                var angleIcoTriangle =
                    (float)Math.Acos(1 - ((torusDiameter * torusDiameter) / (2 * (tubeDiameter * tubeDiameter * .25))));
                var circleAngle = (float)Math.PI + angleIcoTriangle;
                var offset      = -circleAngle / 2;

                // The Cross-Section is defined by only a Segment of a Circle
                crossSectionPoints = GetCircleSegment(pDiv, circleAngle, offset);
            }
            else
            {
                // "normal" Torus (with a Circle as Cross-Section of the Torus
                crossSectionPoints = GetCircle(pDiv, true);
            }

            // Transform crosssection to real Size
            crossSectionPoints = crossSectionPoints
                                 .Select(p => new Vector2(p.X * tubeDiameter * .5f, p.Y * tubeDiameter * .5f)).ToList();

            // Transform the Cross-Section Points to 3D Space
            var crossSection3DPoints = crossSectionPoints.Select(p => new Vector3(p.X, 0, p.Y)).ToList();

            // Add the needed Vertex-Positions of the Torus
            for (var i = 0; i < tDiv; i++)
            {
                // Angle of the current Cross-Section in the XY-Plane
                var angle = EngineMath.PI * 2 * ((float)i / tDiv);

                // Rotate the Cross-Section around the Origin by using the angle and the defined torusDiameter
                var rotatedPoints = crossSection3DPoints.Select(p3D =>
                                                                new Vector3((float)Math.Cos(angle) * (p3D.X + torusDiameter * .5f),
                                                                            (float)Math.Sin(angle) * (p3D.X + torusDiameter * .5f), p3D.Z)).ToList();
                for (var j = 0; j < pDiv; j++)
                {
                    // If selfintersecting Torus, skip the first and last Point of the Cross-Sections, when not the first Cross Section.
                    // We only need the first and last Point of the first Cross-Section once!
                    if (selfIntersecting && i > 0 && (j == 0 || j == (pDiv - 1)))
                    {
                        continue;
                    }

                    // Get position
                    var position = rotatedPoints[j];

                    // Calculate normal
                    var normal = Vector3.Zero;
                    switch (selfIntersecting)
                    {
                    case true when i == 0 && j == 0:
                        normal = new Vector3(0, 0, -1);
                        break;

                    case true when i == 0 && j == (pDiv - 1):
                        normal = new Vector3(0, 0, 1);
                        break;

                    default:
                        var rotatedOrigin =
                            new Vector3((float)Math.Cos(angle) * torusDiameter * .5f,
                                        (float)Math.Sin(angle) * torusDiameter * .5f, 0);
                        normal = Vector3.Normalize(position - rotatedOrigin);
                        break;
                    }

                    // Calculate texture coordinates
                    var   texU  = (float)i / tDiv;
                    float textV = 0;
                    if (i > 0 && selfIntersecting)
                    {
                        textV = (float)(j + 1) / pDiv;
                    }
                    else
                    {
                        textV = (float)j / pDiv;
                    }
                    var texCoord = new Vector2(texU, textV);

                    // Create and add vertex
                    var newVertex = new VertexBasic();
                    newVertex.Position  = position;
                    newVertex.Normal    = normal;
                    newVertex.TexCoord1 = texCoord;
                    target.Owner.AddVertex(newVertex);
                }
            }

            // Add Triangle-Indices
            for (var i = 0; i < tDiv; i++)
            {
                if (!selfIntersecting)
                {
                    // Normal non-selfintersecting Torus
                    // Just add Triangle-Strips between all neighboring Cross-Sections

                    var firstPointIdx           = i * pDiv;
                    var firstPointIdxNextCircle = ((i + 1) % tDiv) * pDiv;
                    for (var j = 0; j < pDiv; j++)
                    {
                        var jNext = (j + 1) % pDiv;

                        target.AddTriangle(
                            firstPointIdx + j + startVertex,
                            firstPointIdx + jNext + startVertex,
                            firstPointIdxNextCircle + j + startVertex);
                        target.AddTriangle(
                            firstPointIdxNextCircle + j + startVertex,
                            firstPointIdx + jNext + startVertex,
                            firstPointIdxNextCircle + jNext + startVertex);
                    }
                }
                else
                {
                    // Selfintersecting Torus

                    // Add intermediate Triangles like for the non-selfintersecting Torus
                    // Skip the first and last Triangles, the "Caps" will be added later
                    // Determine the Index of the first Point of the first Cross-Section
                    var firstPointIdx = i * (pDiv - 2) + 1;
                    firstPointIdx += i > 0 ? 1 : 0;

                    // Determine the Index of the first Point of the next Cross-Section
                    var firstPointIdxNextCircle = pDiv + firstPointIdx - 1;
                    firstPointIdxNextCircle -= i > 0 ? 1 : 0;
                    if (firstPointIdxNextCircle >= target.Owner.CountVertices)
                    {
                        firstPointIdxNextCircle %= target.Owner.CountVertices;
                        firstPointIdxNextCircle++;
                    }

                    // Add Triangles between the "middle" Parts of the neighboring Cross-Sections
                    for (var j = 1; j < pDiv - 2; j++)
                    {
                        target.AddTriangle(
                            firstPointIdx + j - 1 + startVertex,
                            firstPointIdxNextCircle + j - 1 + startVertex,
                            firstPointIdx + j + startVertex);
                        target.AddTriangle(
                            firstPointIdxNextCircle + j - 1 + startVertex,
                            firstPointIdxNextCircle + j + startVertex,
                            firstPointIdx + j + startVertex);
                    }
                }
            }

            // For selfintersecting Tori
            if (selfIntersecting)
            {
                // Add bottom Cap by creating a List of Vertex-Indices
                // and using them to create a Triangle-Fan
                var vertexIndices = new List <int>(tDiv + 1);
                vertexIndices.Add(0);
                for (var i = 0; i < tDiv; i++)
                {
                    if (i == 0)
                    {
                        vertexIndices.Add(1 + startVertex);
                    }
                    else
                    {
                        vertexIndices.Add(pDiv + (i - 1) * (pDiv - 2) + startVertex);
                    }
                }
                vertexIndices.Add(1 + startVertex);
                vertexIndices.Reverse();
                target.AddTriangleFan(vertexIndices);

                // Add top Cap by creating a List of Vertex-Indices
                // and using them to create a Triangle-Fan
                vertexIndices = new List <int>(tDiv + 1);
                vertexIndices.Add(pDiv - 1 + startVertex);
                for (var i = 0; i < tDiv; i++)
                {
                    if (i == 0)
                    {
                        vertexIndices.Add(pDiv - 2 + startVertex);
                    }
                    else
                    {
                        vertexIndices.Add(pDiv + i * (pDiv - 2) - 1 + startVertex);
                    }
                }
                vertexIndices.Add(pDiv - 2 + startVertex);
                target.AddTriangleFan(vertexIndices);
            }

            return(new BuiltVerticesRange(target.Owner, startVertex, target.Owner.CountVertices - startVertex));
        }
 /// <summary>
 /// Builds a cylinder into the geometry with correct texture coordinates and normals.
 /// </summary>
 /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
 /// <param name="radius">The radius of the cylinder.</param>
 /// <param name="height">The height of the cylinder.</param>
 /// <param name="countOfSegments">Total count of segments to generate.</param>
 public static BuiltVerticesRange BuildCylinderBottom(this GeometrySurface target, float radius, float height, int countOfSegments)
 {
     return(target.BuildCylinder(
                new Vector3(0f, -(height / 2f), 0f),
                radius, height, countOfSegments, false, true, false));
 }
 /// <summary>
 /// Builds a cylinder into the geometry with correct texture coordinates and normals.
 /// </summary>
 /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
 /// <param name="bottomMiddle">Coordinate of bottom middle.</param>
 /// <param name="radius">The radius of the cylinder.</param>
 /// <param name="height">The height of the cylinder.</param>
 /// <param name="countOfSegments">Total count of segments to generate.</param>
 public static BuiltVerticesRange BuildCylinderSides(this GeometrySurface target, Vector3 bottomMiddle, float radius, float height, int countOfSegments)
 {
     return(target.BuildCylinder(bottomMiddle, radius, height, countOfSegments, true, false, false));
 }
        /// <summary>
        /// Builds a cylinder into the geometry with correct texture coordinates and normals.
        /// </summary>
        /// <param name="target">Target <see cref="GeometrySurface"/>.</param>
        /// <param name="bottomMiddle">Coordinate of bottom middle.</param>
        /// <param name="radius">The radius of the cylinder.</param>
        /// <param name="height">The height of the cylinder.</param>
        /// <param name="countOfSegments">Total count of segments to generate.</param>
        /// <param name="buildBottom">Build bottom of the cylinder.</param>
        /// <param name="buildSides">Build sides of the cylinder.</param>
        /// <param name="buildTop">Build top side of the cylinder.</param>
        public static BuiltVerticesRange BuildCylinder(
            this GeometrySurface target,
            Vector3 bottomMiddle, float radius, float height, int countOfSegments,
            bool buildSides, bool buildBottom, bool buildTop)
        {
            var startVertex = target.Owner.CountVertices;

            radius          = Math.Max(EngineMath.TOLERANCE_FLOAT_POSITIVE, radius);
            height          = Math.Max(EngineMath.TOLERANCE_FLOAT_POSITIVE, height);
            countOfSegments = Math.Max(3, countOfSegments);

            var diameter = radius * 2f;

            // Get texture offsets
            var texX        = 1f;
            var texY        = 1f;
            var texSegmentY = 1f;
            var texSegmentX = 1f;

            if (target.IsTextureTileModeEnabled(out var tileSize))
            {
                texX        = diameter / tileSize.X;
                texY        = diameter / tileSize.Y;
                texSegmentY = height / tileSize.Y;
                texSegmentX = EngineMath.RAD_180DEG * diameter / tileSize.X;
            }

            // Specify bottom and top middle coordinates
            var bottomCoordinate = bottomMiddle;
            var topCoordinate    = new Vector3(bottomMiddle.X, bottomMiddle.Y + height, bottomMiddle.Z);

            // Create bottom and top vertices
            var bottomVertex = new VertexBasic(bottomCoordinate, new Vector2(texX / 2f, texY / 2f), new Vector3(0f, -1f, 0f));
            var topVertex    = new VertexBasic(topCoordinate, new Vector2(texX / 2f, texY / 2f), new Vector3(0f, 1f, 0f));

            // AddObject bottom and top vertices to the geometry
            var bottomVertexIndex = target.Owner.AddVertex(bottomVertex);
            var topVertexIndex    = target.Owner.AddVertex(topVertex);

            // Generate all segments
            var fullRadian       = EngineMath.RAD_360DEG;
            var countOfSegmentsF = (float)countOfSegments;

            for (var loop = 0; loop < countOfSegments; loop++)
            {
                // Calculate rotation values for each segment border
                var startRadian  = fullRadian * (loop / countOfSegmentsF);
                var targetRadian = fullRadian * ((loop + 1) / countOfSegmentsF);
                var normalRadian = startRadian + (targetRadian - startRadian) / 2f;

                // Generate all normals
                var sideNormal      = Vector3Ex.NormalFromHVRotation(normalRadian, 0f);
                var sideLeftNormal  = Vector3Ex.NormalFromHVRotation(startRadian, 0f);
                var sideRightNormal = Vector3Ex.NormalFromHVRotation(targetRadian, 0f);

                //
                var sideLeftTexCoord  = new Vector2(0.5f + sideLeftNormal.X * radius, 0.5f + sideLeftNormal.Z * radius);
                var sideRightTexCoord = new Vector2(0.5f + sideRightNormal.X * radius, 0.5f + sideRightNormal.Z * radius);

                // Generate all points
                var sideLeftBottomCoord  = bottomCoordinate + sideLeftNormal * radius;
                var sideRightBottomCoord = bottomCoordinate + sideRightNormal * radius;
                var sideLeftTopCoord     = new Vector3(sideLeftBottomCoord.X, sideLeftBottomCoord.Y + height, sideLeftBottomCoord.Z);
                var sideRightTopCoord    = new Vector3(sideRightBottomCoord.X, sideRightBottomCoord.Y + height, sideRightBottomCoord.Z);

                // AddObject segment bottom triangle
                if (buildBottom)
                {
                    var segmentBottomLeft  = bottomVertex.Copy(sideLeftBottomCoord, sideLeftTexCoord);
                    var segmentBottomRight = bottomVertex.Copy(sideRightBottomCoord, sideRightTexCoord);
                    target.AddTriangle(
                        bottomVertexIndex, target.Owner.AddVertex(segmentBottomLeft), target.Owner.AddVertex(segmentBottomRight));
                }

                // AddObject segment top triangle
                if (buildTop)
                {
                    var segmentTopLeft  = topVertex.Copy(sideLeftTopCoord, sideLeftTexCoord);
                    var segmentTopRight = topVertex.Copy(sideRightTopCoord, sideRightTexCoord);
                    target.AddTriangle(
                        topVertexIndex, target.Owner.AddVertex(segmentTopRight), target.Owner.AddVertex(segmentTopLeft));
                }

                if (buildSides)
                {
                    // Calculate texture coords for side segment
                    var texCoordSegmentStart  = new Vector2(texSegmentX * (loop / (float)countOfSegments), 0f);
                    var texCoordSegmentTarget = new Vector2(texSegmentX * ((loop + 1) / (float)countOfSegments), texSegmentY);

                    // AddObject segment side
                    target.BuildRect(sideLeftBottomCoord, sideRightBottomCoord, sideRightTopCoord, sideLeftTopCoord, sideNormal, texCoordSegmentStart, texCoordSegmentTarget);
                }
            }

            return(new BuiltVerticesRange(target.Owner, startVertex, target.Owner.CountVertices - startVertex));
        }