Advertisement
MxrFxrr

Subdivided Octahedron Sphere (with Seam Fix)

Aug 12th, 2012
541
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 11.79 KB | None | 0 0
  1. //Copyright (C) 2012 Carsten Jørgensen
  2. //Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
  3. //associated documentation files (the "Software"), to deal in the Software without restriction, including
  4. //without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  5. //copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
  6. //the following conditions:
  7.  
  8. //The above copyright notice and this permission notice shall be included in all copies or substantial
  9. //portions of the Software.
  10.  
  11. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
  12. //LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  13. //NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  14. //WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  15. //SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  16.  
  17. using System;
  18. using System.Collections.Generic;
  19. using Microsoft.Xna.Framework;
  20. using Microsoft.Xna.Framework.Graphics;
  21.  
  22. namespace MyGame
  23. {
  24.     /// <summary>
  25.     /// Generates a unit sphere, by subdividing an octahedron
  26.     /// </summary>
  27.     public class OctahedronSphere
  28.     {
  29.         /// <summary>
  30.         /// The triangle class. A collection of 3 indices that represent a triangle.
  31.         /// Could simply be replaced by int[] in its use, but it clarifies/prettifies the code a bit.
  32.         /// </summary>
  33.         private class Triangle
  34.         {
  35.             private int[] indices;
  36.             public int this[int triIndex]
  37.             {
  38.                 get { return indices[triIndex]; }
  39.                 set { indices[triIndex] = value; }
  40.             }
  41.  
  42.             public Triangle(int v0, int v1, int v2)
  43.             {
  44.                 indices = new int[] { v0, v1, v2 };
  45.             }
  46.         }
  47.  
  48.         #region Properties and Fields
  49.  
  50.         public int Tesselation { get; set; }
  51.  
  52.         protected VertexBuffer VertexBuffer { get; set; }
  53.         protected IndexBuffer IndexBuffer { get; set; }
  54.         protected VertexPositionTexture[] Vertices { get; set; }
  55.         protected int[] Indices { get; set; }
  56.  
  57.         private List<Triangle> triangles;
  58.         private List<Vector3> points;
  59.  
  60.         #endregion
  61.  
  62.         #region Constructors
  63.  
  64.         public OctahedronSphere() { }
  65.         public OctahedronSphere(int tesselation)
  66.         {
  67.             Tesselation = tesselation;
  68.         }
  69.  
  70.         #endregion
  71.  
  72.         /// <summary>
  73.         /// Creates vertices and indices for a unit sphere by subdividing an octahedron
  74.         /// </summary>
  75.         public void Create(GraphicsDevice graphicsDevice)
  76.         {
  77.             //Create the points and triangles for the initial base octahedron
  78.             CreateOctahedron();
  79.  
  80.             //Divide each triangle 4 times, each iteration (Tesselation = Iterations)
  81.             for (int i = 0; i < Tesselation; i++)
  82.             {
  83.                 List<Triangle> newtriangles = new List<Triangle>();
  84.                 foreach (Triangle triangle in triangles)
  85.                 {
  86.                     //Adds 4 new triangles to the new collection:
  87.                     newtriangles.AddRange(DivideTriangle(triangle));
  88.                 }
  89.                 triangles.Clear();
  90.                 triangles = newtriangles;
  91.             }
  92.  
  93.             //2nd pass sets the Vertices and Indices for the primitive:
  94.             SetMesh(graphicsDevice);
  95.         }
  96.  
  97.         /// <summary>
  98.         /// The 2nd pass. Runs through all triangles and adds the Vertices and Indices.
  99.         /// </summary>
  100.         private void SetMesh(GraphicsDevice graphicsDevice)
  101.         {
  102.             //Add the vertices based on the list of triangles:
  103.             int cloneCount = (int)(1.0 / 2.0 * (Math.Pow(2, Tesselation + 2) - 2));
  104.             Vertices = new VertexPositionTexture[points.Count + cloneCount];
  105.             for (int i = 0; i < triangles.Count; i++)
  106.             {
  107.                 //Adds vertices, and tacles seam issue:
  108.                 AddTriangleVertices(triangles[i]);
  109.             }
  110.  
  111.             VertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionTexture), Vertices.Length, BufferUsage.WriteOnly);
  112.             VertexBuffer.SetData(Vertices);
  113.  
  114.             //Creates an array of indices based on the list of triangles:
  115.             Indices = new int[triangles.Count * 3];
  116.             for (int i = 0; i < triangles.Count; i++)
  117.             {
  118.                 Indices[i * 3 + 0] = triangles[i][0];
  119.                 Indices[i * 3 + 1] = triangles[i][1];
  120.                 Indices[i * 3 + 2] = triangles[i][2];
  121.             }
  122.  
  123.             IndexBuffer = new IndexBuffer(graphicsDevice, typeof(int), Indices.Length, BufferUsage.WriteOnly);
  124.             IndexBuffer.SetData(Indices);
  125.         }
  126.  
  127.         /// <summary>
  128.         /// Draws the octahedron sphere immediately
  129.         /// </summary>
  130.         public void Draw(GraphicsDevice graphicsDevice, Effect effect)
  131.         {
  132.             //Set the Vertex and Index buffers created by SetMesh()
  133.             graphicsDevice.SetVertexBuffer(VertexBuffer);
  134.             graphicsDevice.Indices = IndexBuffer;
  135.             graphicsDevice.RasterizerState = RasterizerState.CullNone;
  136.  
  137.             //Apply effect and draw:
  138.             effect.CurrentTechnique.Passes[0].Apply();
  139.             graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, Vertices.Length, 0, Indices.Length / 3);
  140.         }
  141.         public void Draw(GraphicsDevice graphicsDevice, BasicEffect effect,
  142.             Matrix world, Matrix view, Matrix projection)
  143.         {
  144.             effect.World = world;
  145.             effect.View = view;
  146.             effect.Projection = projection;
  147.  
  148.             Draw(graphicsDevice, effect);
  149.         }
  150.  
  151.         #region Helpers
  152.  
  153.         /// <summary>
  154.         /// Sets the starting points and triangles for the base octahedron
  155.         /// </summary>
  156.         private void CreateOctahedron()
  157.         {
  158.             //The 6 Octahedron vertices:
  159.             points = new List<Vector3>() {
  160.                 new Vector3(0, 1, 0),
  161.                 new Vector3(1, 0, 0),
  162.                 new Vector3(0, 0, -1),
  163.                 new Vector3(-1, 0, 0),
  164.                 new Vector3(0, 0, 1),
  165.                 new Vector3(0,-1, 0)
  166.             };
  167.  
  168.             //The 8 Octahedron triangles (indexed triangles)
  169.             triangles = new List<Triangle>()
  170.             {
  171.                 new Triangle(0,1,2),new Triangle(0,2,3),new Triangle(0,3,4),new Triangle(0,4,1),
  172.                 new Triangle(5,1,2),new Triangle(5,2,3),new Triangle(5,3,4),new Triangle(5,4,1)
  173.             };
  174.         }
  175.  
  176.         /// <summary>
  177.         /// Divides a triangle in 4 smaller triangles, by connecting the centers of each edge.
  178.         /// </summary>
  179.         private Triangle[] DivideTriangle(Triangle triangle)
  180.         {
  181.             //Calculate the new points that's needed for creating additional triangles:
  182.             Vector3[] newPoints = new Vector3[] {
  183.                 NormalizedEdgeCentroid(points[triangle[0]], points[triangle[1]]),
  184.                 NormalizedEdgeCentroid(points[triangle[1]], points[triangle[2]]),
  185.                 NormalizedEdgeCentroid(points[triangle[2]], points[triangle[0]])
  186.             };
  187.  
  188.             //Runs through the new points to retrieve indices:
  189.             int[] newIndices = new int[3];
  190.             for (int i = 0; i < 3; i++)
  191.             {
  192.                 //Finds index for this point:
  193.                 newIndices[i] = points.FindIndex(v => v == newPoints[i]);
  194.  
  195.                 //If point does not exists, it will be added to the collection:
  196.                 if (newIndices[i] < 0)
  197.                 {
  198.                     //The index is set to last point (last index = count - 1):
  199.                     newIndices[i] = points.Count;
  200.                     points.Add(newPoints[i]);
  201.                 }
  202.             }
  203.  
  204.             //Return 4 subdivided triangles:
  205.             return new Triangle[] {
  206.                 new Triangle(newIndices[0], newIndices[1], newIndices[2]),
  207.                 new Triangle(triangle[0], newIndices[0], newIndices[2]),
  208.                 new Triangle(triangle[1], newIndices[0], newIndices[1]),
  209.                 new Triangle(triangle[2], newIndices[1], newIndices[2])
  210.             };
  211.         }
  212.  
  213.         /// <summary>
  214.         /// Adds vertices to the collection based on a triangle. Also corrects seam issue.
  215.         /// </summary>
  216.         private void AddTriangleVertices(Triangle triangle)
  217.         {
  218.             //Calculate texture coordinates for each vertex of this triangle:
  219.             Vector2[] texCoord = new Vector2[] {
  220.                 MapUv(points[triangle[0]]),
  221.                 MapUv(points[triangle[1]]),
  222.                 MapUv(points[triangle[2]])
  223.             };
  224.  
  225.             //Get the indices which is on the seam:
  226.             int[] seamIndices = GetSeamIndices(texCoord);
  227.             if (seamIndices != null)
  228.             {
  229.                 for (int i = 0; i < seamIndices.Length; i++)
  230.                 {
  231.                     int triangleIndex = seamIndices[i];
  232.                     int currentIndex = triangle[triangleIndex];
  233.                     Vector3 clone = points[currentIndex];
  234.  
  235.                     //Checks if duplicate allready exists (begins search after the current index):
  236.                     int cloneIndex = points.FindIndex(currentIndex + 1, x => x == clone);
  237.                     if (cloneIndex == -1)
  238.                     {
  239.                         points.Add(clone);
  240.                         cloneIndex = points.Count - 1;
  241.                     }
  242.  
  243.                     //Change the triangle, to use the new index, and adjust texture U value to 1:
  244.                     triangle[triangleIndex] = cloneIndex;
  245.                     texCoord[triangleIndex].X = 1;
  246.                 }
  247.             }
  248.  
  249.             //Adds the vertices:
  250.             for (int i = 0; i < 3; i++)
  251.             {
  252.                 Vertices[triangle[i]] = new VertexPositionTexture()
  253.                 {
  254.                     Position = points[triangle[i]],
  255.                     TextureCoordinate = texCoord[i]
  256.                 };
  257.             }
  258.         }
  259.  
  260.         /// <summary>
  261.         /// Returns how many (if any) of the provided coords are located on the seam (U = 0),
  262.         /// while the remainding of the coords are closer to 1 than 0.
  263.         /// </summary>
  264.         private int[] GetSeamIndices(Vector2[] texCoords)
  265.         {
  266.             List<int> seamIndices = new List<int>();
  267.  
  268.             //Loop through the coords
  269.             for (int i = 0; i < 3; i++)
  270.             {
  271.                 //Add index if U has a value of 0:
  272.                 if (texCoords[i].X == 0)
  273.                     seamIndices.Add(i);
  274.  
  275.                 //If at any point, a texcoord is on the first half of the sphere, this is not a seam triangle:
  276.                 else if (texCoords[i].X < 0.5f)
  277.                     return null;
  278.             }
  279.  
  280.             return seamIndices.Count > 0 ? seamIndices.ToArray() : null;
  281.         }
  282.  
  283.         /// <summary>
  284.         /// Returns the normalized center of a line
  285.         /// </summary>
  286.         private Vector3 NormalizedEdgeCentroid(Vector3 p0, Vector3 p1)
  287.         {
  288.             return Vector3.Normalize(new Vector3(
  289.                 (p0.X + p1.X) / 2,
  290.                 (p0.Y + p1.Y) / 2,
  291.                 (p0.Z + p1.Z) / 2));
  292.         }
  293.  
  294.         /// <summary>
  295.         /// UV maps a normalized Vector3.
  296.         /// </summary>
  297.         private Vector2 MapUv(Vector3 p)
  298.         {
  299.             float u = 0.5f - ((float)Math.Atan2(p.Z, p.X) / MathHelper.TwoPi);
  300.             float v = 0.5f - 2.0f * ((float)Math.Asin(p.Y) / MathHelper.TwoPi);
  301.  
  302.             return new Vector2(u, v);
  303.         }
  304.  
  305.         #endregion
  306.     }
  307. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement