/// <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))); }
/// <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))); }
/// <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); }
/// <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); }
/// <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)); }
/// <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)); }
/// <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); }
/// <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))); }
/// <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())); }
/// <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)); }
/// <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)); }