77 using System.Collections.Generic;
78 using SiliconStudio.Core;
79 using SiliconStudio.Core.Mathematics;
81 namespace SiliconStudio.
Paradox.Graphics
83 public partial class GeometricPrimitive
94 private static readonly
Vector3[] OctahedronVertices =
new Vector3[]
105 private static readonly
int[] OctahedronIndices =
new int[]
117 private List<Vector3> vertexPositions;
119 private List<int> indexList;
121 private List<VertexPositionNormalTexture> vertices;
123 private unsafe
int* indices;
140 return new GeometricPrimitive(graphicsDevice, New(diameter, tessellation, toLeftHanded));
150 public static GeometricMeshData<VertexPositionNormalTexture>
New(
float diameter = 1.0f,
int tessellation = 3,
bool toLeftHanded =
false)
153 return sphere.Create(diameter, tessellation, toLeftHanded);
163 private unsafe GeometricMeshData<VertexPositionNormalTexture> Create(
float diameter = 1.0f,
int tessellation = 3,
bool toLeftHanded =
false)
165 subdividedEdges =
new Dictionary<UndirectedEdge, int>();
167 float radius = diameter / 2.0f;
170 vertexPositions =
new List<Vector3>(OctahedronVertices);
171 indexList =
new List<int>(OctahedronIndices);
176 const int northPoleIndex = 0;
177 const int southPoleIndex = 5;
179 for (
int iSubdivision = 0; iSubdivision < tessellation; ++iSubdivision)
182 var newIndices =
new List<int>();
183 subdividedEdges.Clear();
185 int triangleCount = indexList.Count / 3;
186 for (
int iTriangle = 0; iTriangle < triangleCount; ++iTriangle)
192 int iv0 = indexList[iTriangle * 3 + 0];
193 int iv1 = indexList[iTriangle * 3 + 1];
194 int iv2 = indexList[iTriangle * 3 + 2];
210 DivideEdge(iv0, iv1, out v01, out iv01);
211 DivideEdge(iv1, iv2, out v12, out iv12);
212 DivideEdge(iv0, iv2, out v20, out iv20);
225 newIndices.Add(iv01);
226 newIndices.Add(iv20);
229 newIndices.Add(iv20);
230 newIndices.Add(iv12);
234 newIndices.Add(iv20);
235 newIndices.Add(iv01);
236 newIndices.Add(iv12);
239 newIndices.Add(iv01);
241 newIndices.Add(iv12);
245 indexList.AddRange(newIndices);
249 vertices =
new List<VertexPositionNormalTexture>(vertexPositions.Count);
250 for (
int i = 0; i < vertexPositions.Count; i++)
252 var vertexValue = vertexPositions[i];
254 var normal = vertexValue;
257 var pos = normal * radius;
260 float longitude = (float)Math.Atan2(normal.X, -normal.Z);
261 float latitude = (float)Math.Acos(normal.Y);
263 float u = (float)(longitude / (Math.PI * 2.0) + 0.5);
264 float v = (float)(latitude / Math.PI);
266 var texcoord =
new Vector2(1.0f - u, v);
267 vertices.Add(
new VertexPositionNormalTexture(pos, normal, texcoord));
270 const float XMVectorSplatEpsilon = 1.192092896e-7f;
282 int preCount = vertices.Count;
283 var indicesArray = indexList.ToArray();
284 fixed (
void* pIndices = indicesArray)
286 indices = (
int*)pIndices;
288 for (
int i = 0; i < preCount; ++i)
291 bool isOnPrimeMeridian = MathUtil.WithinEpsilon(vertices[i].Position.X, 0, XMVectorSplatEpsilon)
294 if (isOnPrimeMeridian)
296 int newIndex = vertices.Count;
299 VertexPositionNormalTexture v = vertices[i];
300 v.TextureCoordinate.X = 1.0f;
304 for (
int j = 0; j < indexList.Count; j += 3)
306 var triIndex0 = &indices[j + 0];
307 var triIndex1 = &indices[j + 1];
308 var triIndex2 = &indices[j + 2];
314 else if (*triIndex1 == i)
316 Utilities.Swap(ref *triIndex0, ref *triIndex1);
318 else if (*triIndex2 == i)
320 Utilities.Swap(ref *triIndex0, ref *triIndex2);
329 if (Math.Abs(vertices[*triIndex0].TextureCoordinate.X - vertices[*triIndex1].TextureCoordinate.X) > 0.5f ||
330 Math.Abs(vertices[*triIndex0].TextureCoordinate.X - vertices[*triIndex2].TextureCoordinate.X) > 0.5f)
333 indices[j + 0] = newIndex;
339 FixPole(northPoleIndex);
340 FixPole(southPoleIndex);
346 return new GeometricMeshData<VertexPositionNormalTexture>(vertices.ToArray(), indexList.ToArray(), toLeftHanded) {Name =
"GeoSphere"};
349 private unsafe
void FixPole(
int poleIndex)
351 var poleVertex = vertices[poleIndex];
352 bool overwrittenPoleVertex =
false;
354 for (ushort i = 0; i < indexList.Count; i += 3)
362 if (indices[i + 0] == poleIndex)
364 pPoleIndex = &indices[i + 0];
365 pOtherIndex0 = &indices[i + 1];
366 pOtherIndex1 = &indices[i + 2];
368 else if (indices[i + 1] == poleIndex)
370 pPoleIndex = &indices[i + 1];
371 pOtherIndex0 = &indices[i + 2];
372 pOtherIndex1 = &indices[i + 0];
374 else if (indices[i + 2] == poleIndex)
376 pPoleIndex = &indices[i + 2];
377 pOtherIndex0 = &indices[i + 0];
378 pOtherIndex1 = &indices[i + 1];
386 var newPoleVertex = poleVertex;
387 newPoleVertex.TextureCoordinate.X = (vertices[*pOtherIndex0].TextureCoordinate.X + vertices[*pOtherIndex1].TextureCoordinate.X) * 0.5f;
388 newPoleVertex.TextureCoordinate.Y = poleVertex.TextureCoordinate.Y;
390 if (!overwrittenPoleVertex)
392 vertices[poleIndex] = newPoleVertex;
393 overwrittenPoleVertex =
true;
397 *pPoleIndex = vertices.Count;
398 vertices.Add(newPoleVertex);
404 private void DivideEdge(
int i0,
int i1, out
Vector3 outVertex, out
int outIndex)
406 var edge =
new UndirectedEdge(i0, i1);
409 if (subdividedEdges.TryGetValue(edge, out outIndex))
412 outVertex = vertexPositions[outIndex];
419 outVertex = (vertexPositions[i0] + vertexPositions[i1])*0.5f;
420 outIndex = vertexPositions.Count;
421 vertexPositions.Add(outVertex);
424 subdividedEdges[edge] = outIndex;
430 private struct UndirectedEdge : IEquatable<UndirectedEdge>
432 public UndirectedEdge(
int item1,
int item2)
436 Item1 = Math.Max(item1, item2);
437 Item2 = Math.Min(item1, item2);
440 public readonly
int Item1;
442 public readonly
int Item2;
444 public bool Equals(UndirectedEdge other)
446 return Item1 == other.Item1 && Item2 == other.Item2;
449 public override bool Equals(
object obj)
451 if (ReferenceEquals(null, obj))
return false;
452 return obj is UndirectedEdge && Equals((UndirectedEdge)obj);
455 public override int GetHashCode()
459 return (Item1.GetHashCode() * 397) ^ Item2.GetHashCode();
463 public static bool operator ==(UndirectedEdge left, UndirectedEdge right)
465 return left.Equals(right);
468 public static bool operator !=(UndirectedEdge left, UndirectedEdge right)
470 return !left.Equals(right);
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
static GeometricMeshData< VertexPositionNormalTexture > New(float diameter=1.0f, int tessellation=3, bool toLeftHanded=false)
Creates a Geodesic sphere.
static GeometricPrimitive New(GraphicsDevice graphicsDevice, float diameter=1.0f, int tessellation=3, bool toLeftHanded=false)
Creates a Geodesic sphere.
A geometric primitive. Use Cube, Cylinder, GeoSphere, Plane, Sphere, Teapot, Torus. See Draw+vertices to learn how to use it.
Represents a three dimensional mathematical vector.
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
static bool WithinEpsilon(float a, float b, float epsilon)
Checks if a - b are almost equals within a float epsilon.
Dictionary< UndirectedEdge, int > subdividedEdges
SiliconStudio.Core.Mathematics.Vector3 Vector3