PageRenderTime 49ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Rendering/Models/GeometryHelper.cs

#
C# | 382 lines | 219 code | 28 blank | 135 comment | 9 complexity | 84ce793b3fde845f32245c0f7e070ddf MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using Delta.ContentSystem.Rendering;
  3. using Delta.Utilities.Graphics;
  4. using Delta.Utilities.Datatypes;
  5. using Delta.Utilities.Helpers;
  6. namespace Delta.Rendering.Models
  7. {
  8. /// <summary>
  9. /// Helper class to generate geometry based on the GeometryData class.
  10. /// This class is derived from GeometryData and can be used as well, it just
  11. /// adds a few more static Create methods.
  12. /// <para />
  13. /// Note: That class has only static method but can't be a static class
  14. /// because it's derived from the GeometryData to extend its create
  15. /// functionality.
  16. /// </summary>
  17. public class GeometryHelper : GeometryData
  18. {
  19. #region CreatePlane
  20. /// <summary>
  21. /// Create plane
  22. /// </summary>
  23. /// <param name="vertexFormat">The vertex format.</param>
  24. /// <param name="setWidth">Width of the set.</param>
  25. /// <param name="setHeight">Height of the set.</param>
  26. /// <returns></returns>
  27. public static GeometryData CreatePlane(VertexFormat vertexFormat,
  28. float setWidth, float setHeight)
  29. {
  30. return CreatePlane(vertexFormat, setWidth, setHeight, Color.White);
  31. } // CreatePlane(vertexFormat, setWidth, setHeight)
  32. /// <summary>
  33. /// Creates an XY plane in the specified vertex format (you can use
  34. /// one of the predefined vertex formats from VertexData).
  35. /// </summary>
  36. /// <param name="vertexFormat">The vertex format.</param>
  37. /// <param name="setWidth">Set width.</param>
  38. /// <param name="setHeight">Set Height.</param>
  39. /// <param name="setColor">Set Color.</param>
  40. /// <returns></returns>
  41. public static GeometryData CreatePlane(VertexFormat vertexFormat,
  42. float setWidth, float setHeight, Color setColor)
  43. {
  44. float halfWidth = setWidth * 0.5f;
  45. float halfDepth = setHeight * 0.5f;
  46. float height = 0;
  47. // We use 4 vertices with 6 indices (0, 1, 2, 2, 1, 3):
  48. // 0 - 1
  49. // | / |
  50. // 2 - 3
  51. GeometryData geometry = new GeometryData(
  52. "<GeneratedPlane(" + setWidth + "x" + setHeight + ")>",
  53. 4, vertexFormat, 6, true, false);
  54. Vector planeNormal = new Vector(0, 0, 1);
  55. Vector planeTangent = new Vector(1, 0, 0);
  56. // Front left
  57. geometry.SetVertexData(0,
  58. new Vector(-halfWidth, halfDepth, height),
  59. new Point(0, 0), setColor, planeNormal, planeTangent);
  60. // Front right
  61. geometry.SetVertexData(1,
  62. new Vector(halfWidth, halfDepth, height),
  63. new Point(1, 0), setColor, planeNormal, planeTangent);
  64. // Rear left
  65. geometry.SetVertexData(2,
  66. new Vector(-halfWidth, -halfDepth, height),
  67. new Point(0, 1), setColor, planeNormal, planeTangent);
  68. // Rear right
  69. geometry.SetVertexData(3,
  70. new Vector(halfWidth, -halfDepth, height),
  71. new Point(1, 1), setColor, planeNormal, planeTangent);
  72. // Top left polygon (Note: Counter-Clockwise is front)
  73. geometry.Indices[0] = 0;
  74. geometry.Indices[1] = 2;
  75. geometry.Indices[2] = 1;
  76. // Bottom right polygon
  77. geometry.Indices[3] = 2;
  78. geometry.Indices[4] = 3;
  79. geometry.Indices[5] = 1;
  80. return geometry;
  81. } // CreatePlane(vertexFormat, setWidth, setHeight)
  82. #endregion
  83. #region CreateSegmentedPlane
  84. /// <summary>
  85. /// Create plane
  86. /// </summary>
  87. /// <param name="vertexFormat">The vertex format.</param>
  88. /// <param name="setWidth">Width of the set.</param>
  89. /// <param name="setHeight">Height of the set.</param>
  90. /// <param name="segments">The segments.</param>
  91. /// <returns></returns>
  92. public static GeometryData CreateSegmentedPlane(VertexFormat vertexFormat,
  93. float setWidth, float setHeight, int segments)
  94. {
  95. return CreateSegmentedPlane(vertexFormat, setWidth, setHeight, segments,
  96. Color.White);
  97. } // CreatePlane(vertexFormat, setWidth, setHeight)
  98. /// <summary>
  99. /// Creates an XY plane in the specified vertex format (you can use
  100. /// one of the predefined vertex formats from VertexData).
  101. /// </summary>
  102. /// <param name="vertexFormat">The vertex format.</param>
  103. /// <param name="setWidth">Sets Width.</param>
  104. /// <param name="setHeight">Sets Height.</param>
  105. /// <param name="segments">The segments.</param>
  106. /// <param name="setColor">Sets Color.</param>
  107. /// <returns>
  108. /// Geometry Data
  109. /// </returns>
  110. public static GeometryData CreateSegmentedPlane(VertexFormat vertexFormat,
  111. float setWidth, float setHeight, int segments, Color setColor)
  112. {
  113. if (segments < 1)
  114. {
  115. throw new InvalidOperationException("You need at least one segment " +
  116. "for CreateSegmentedPlane!");
  117. }
  118. float halfWidth = setWidth * 0.5f;
  119. float halfDepth = setHeight * 0.5f;
  120. float height = 0;
  121. // We use 4 vertices with 6 indices (0, 1, 2, 2, 1, 3):
  122. // 0 - 1
  123. // | / |
  124. // 2 - 3
  125. // But duplicate this many times for as many segments in each direction.
  126. int totalSegments = segments * segments;
  127. int segmentStride = segments + 1;
  128. int totalPoints = segmentStride * segmentStride;
  129. GeometryData geometry = new GeometryData(
  130. "<GeneratedSegmentedPlane(" + setWidth + "x" + setHeight + ")>",
  131. totalPoints, vertexFormat, 6 * totalSegments, true, false);
  132. Vector planeNormal = new Vector(0, 0, 1);
  133. Vector planeTangent = new Vector(1, 0, 0);
  134. // For each segment + 1, build the points
  135. int pointIndex = 0;
  136. for (int ySegment = 0; ySegment < segments + 1; ySegment++)
  137. {
  138. for (int xSegment = 0; xSegment < segments + 1; xSegment++)
  139. {
  140. float xFactor = (float)xSegment / (float)segments;
  141. float yFactor = (float)ySegment / (float)segments;
  142. geometry.SetVertexData(pointIndex++,
  143. new Vector(-halfWidth + xFactor * setWidth,
  144. halfDepth - yFactor * setHeight, height),
  145. new Point(xFactor, yFactor), setColor, planeNormal, planeTangent);
  146. } // for
  147. } // for
  148. // For each segment, build up the 2 triangles
  149. int index = 0;
  150. for (int ySegment = 0; ySegment < segments; ySegment++)
  151. {
  152. for (int xSegment = 0; xSegment < segments; xSegment++)
  153. {
  154. // Top left polygon (Note: Counter-Clockwise is front)
  155. geometry.Indices[index++] = //0,0
  156. (ushort)(xSegment + 0 + (ySegment + 0) * segmentStride);
  157. geometry.Indices[index++] = //0,1
  158. (ushort)(xSegment + 0 + (ySegment + 1) * segmentStride);
  159. geometry.Indices[index++] = //1,0
  160. (ushort)(xSegment + 1 + (ySegment + 0) * segmentStride);
  161. // Bottom right polygon
  162. geometry.Indices[index++] = //0,1
  163. (ushort)(xSegment + 0 + (ySegment + 1) * segmentStride);
  164. geometry.Indices[index++] = //1,1
  165. (ushort)(xSegment + 1 + (ySegment + 1) * segmentStride);
  166. geometry.Indices[index++] = //1,0
  167. (ushort)(xSegment + 1 + (ySegment + 0) * segmentStride);
  168. } // for
  169. } // for
  170. return geometry;
  171. } // CreateSegmentedPlane(vertexFormat, setWidth, setHeight)
  172. #endregion
  173. #region CreateCube
  174. /// <summary>
  175. /// Generates a cube mesh with the given sizes and a material. Cube meshes
  176. /// have their pivot point at Vector.Zero (to make them easier to place).
  177. /// An inverted box with fewer vertices than the normal box.
  178. /// </summary>
  179. /// <param name="vertexFormat">The vertex format.</param>
  180. /// <param name="width">The width in the X.</param>
  181. /// <param name="depth">The depth in the Z.</param>
  182. /// <param name="height">The height in the Y.</param>
  183. /// <returns>
  184. /// Geometry Data
  185. /// </returns>
  186. public static GeometryData CreateCube(VertexFormat vertexFormat,
  187. float width, float depth, float height)
  188. {
  189. return CreateCube(vertexFormat, width, depth, height,
  190. "<GeneratedBox(" + width + ", " + depth + ", " + height + ")>",
  191. height / 2, Color.White);
  192. } // CreateBox(vertexFormat, width, depth)
  193. /// <summary>
  194. /// Generates a Cube mesh with a given pivot point height (usually used
  195. /// to generate the box at Vector.Zero, see other overloads).
  196. /// An inverted box with fewer vertices than the normal box.
  197. /// </summary>
  198. /// <param name="vertexFormat">The vertex format.</param>
  199. /// <param name="width">The width.</param>
  200. /// <param name="depth">The depth.</param>
  201. /// <param name="height">The height.</param>
  202. /// <param name="meshName">Name of the mesh.</param>
  203. /// <param name="heightOffset">The height offset.</param>
  204. /// <param name="setColor">Color of the set.</param>
  205. /// <returns>
  206. /// Geometry Data
  207. /// </returns>
  208. public static GeometryData CreateCube(VertexFormat vertexFormat,
  209. float width, float depth, float height, string meshName,
  210. float heightOffset, Color setColor)
  211. {
  212. float right = width / 2.0f;
  213. float back = depth / 2.0f;
  214. float top = height / 2.0f;
  215. float bottom = -height / 2.0f;
  216. GeometryData geometry = new GeometryData(meshName, 8, vertexFormat,
  217. 6 * 6, true, false);
  218. // First go to the start position for this vertex
  219. geometry.Format.Elements[0].SaveData(geometry.writer,
  220. new Vector(-right, -back, top));
  221. geometry.Format.Elements[0].SaveData(geometry.writer,
  222. new Vector(right, -back, top));
  223. geometry.Format.Elements[0].SaveData(geometry.writer,
  224. new Vector(-right, -back, bottom));
  225. geometry.Format.Elements[0].SaveData(geometry.writer,
  226. new Vector(right, -back, bottom));
  227. geometry.Format.Elements[0].SaveData(geometry.writer,
  228. new Vector(right, back, top));
  229. geometry.Format.Elements[0].SaveData(geometry.writer,
  230. new Vector(-right, back, top));
  231. geometry.Format.Elements[0].SaveData(geometry.writer,
  232. new Vector(right, back, bottom));
  233. geometry.Format.Elements[0].SaveData(geometry.writer,
  234. new Vector(-right, back, bottom));
  235. // Now go through all the faces and generate the indices (see comments)
  236. // Indices are reversed so that the Cube is visible when you are inside it.
  237. geometry.Indices = new ushort[]
  238. {
  239. // Front face quad (0, 1, 2, 2, 1, 3)
  240. 0, 1, 2, 2, 1, 3,
  241. // Back face quad (4, 5, 6, 6, 5, 7)
  242. 4, 5, 6, 6, 5, 7,
  243. // Left face quad (5, 0, 7, 7, 0, 2)
  244. 5, 0, 7, 7, 0, 2,
  245. // Right face quad (1, 4, 3, 3, 4, 6)
  246. 1, 4, 3, 3, 4, 6,
  247. // Upper face quad (5, 4, 0, 0, 4, 1,)
  248. 5, 4, 0, 0, 4, 1,
  249. // Lower face quad (6, 7, 3, 3, 7, 2,)
  250. 6, 7, 3, 3, 7, 2,
  251. };
  252. return geometry;
  253. } // CreateCube(vertexFormat, width, depth)
  254. #endregion
  255. #region CreateSphere
  256. /// <summary>
  257. /// Creates a sphere.
  258. /// </summary>
  259. /// <param name="vertexFormat">The vertex format.</param>
  260. /// <param name="radius">The radius.</param>
  261. /// <param name="setColor">Color of the set.</param>
  262. /// <returns></returns>
  263. public static GeometryData CreateSphere(VertexFormat vertexFormat,
  264. float radius, Color setColor)
  265. {
  266. // Set up the subdivision in which we build the geometry of the sphere.
  267. // The tessellation indicates in how many segments the sphere is
  268. // interpolated, smaller spheres have less tessellation, bigger spheres
  269. // use more tessellation, but we keep it between 4 and 32.
  270. int tessellation = (int)(MathHelper.Sqrt(radius) * 6.0f);
  271. tessellation = MathHelper.Clamp(tessellation, 6, 32);
  272. // Make multiple of 3 for better fitting texturing
  273. tessellation = (tessellation / 3) * 3;
  274. int verticalSegments = tessellation;
  275. // Add one horizontal segment to fit around a circle, good for UVs
  276. int horizontalSegments = tessellation * 2 + 1;
  277. // Create rings of vertices at progressively higher latitudes.
  278. GeometryData geometry = new GeometryData(
  279. "<GeneratedSphere(" + radius + ")>",
  280. verticalSegments * horizontalSegments,
  281. vertexFormat, 6 * (verticalSegments - 1) * horizontalSegments,
  282. true, false);
  283. for (int index = 0; index < verticalSegments; index++)
  284. {
  285. // Lets begin with the latitude and divide each segment. As we are
  286. // using circular surface, we will split each position along the
  287. // vertical axis with the cosine. That is every position in the
  288. // vertical segment creates a ring with a maximum width stated by the
  289. // cosine (at the top width=0, in the medium reaches the maximum
  290. // width = 1, and at the bottom width=0).
  291. float latitude = index * 180.0f / (float)(verticalSegments - 1) - 90.0f;
  292. float dy = MathHelper.Sin(latitude);
  293. float dxz = MathHelper.Cos(latitude);
  294. // Create a single ring of vertices at this latitude.
  295. for (int j = 0; j < horizontalSegments; j++)
  296. {
  297. // Next step is tessellation along horizontal axis in which we just
  298. // simple indicates the position of each vertex in the ring with the
  299. // previously established width along the surface of the sphere
  300. float longitude = j * 360.0f / (float)(horizontalSegments - 1);
  301. float dx = MathHelper.Cos(longitude) * dxz;
  302. float dz = MathHelper.Sin(longitude) * dxz;
  303. // finally we got the correct position
  304. Vector normal = new Vector(dx, dy, dz);
  305. // and we assign the corresponding U,V coordinate of the texture
  306. // in a way that for each circle of the sphere it contains a line
  307. // of the texture image
  308. geometry.SetVertexData(
  309. index * horizontalSegments + j,
  310. normal * radius,
  311. new Point((float)j / (float)(horizontalSegments - 1),
  312. (float)index / (float)(verticalSegments - 1)), setColor, normal,
  313. Vector.Cross(normal, Vector.UnitZ));
  314. } // for
  315. } // for
  316. // Create a fan connecting the bottom vertex to the bottom latitude ring
  317. // and finally set up the indices connecting each vertex.
  318. // Fill the sphere body with triangles joining each pair of rings.
  319. int num = 0;
  320. for (int index = 0; index < verticalSegments - 1; index++)
  321. {
  322. for (int j = 0; j < horizontalSegments; j++)
  323. {
  324. int nextI = (index + 1);
  325. int nextJ = (j + 1) % horizontalSegments;
  326. // Note: Counter-Clockwise is front
  327. geometry.Indices[num++] =
  328. (ushort)(index * horizontalSegments + j);
  329. geometry.Indices[num++] =
  330. (ushort)(nextI * horizontalSegments + j);
  331. geometry.Indices[num++] =
  332. (ushort)(index * horizontalSegments + nextJ);
  333. geometry.Indices[num++] =
  334. (ushort)(index * horizontalSegments + nextJ);
  335. geometry.Indices[num++] =
  336. (ushort)(nextI * horizontalSegments + j);
  337. geometry.Indices[num++] =
  338. (ushort)(nextI * horizontalSegments + nextJ);
  339. } // for
  340. } // for
  341. return geometry;
  342. } // CreateSphere(vertexFormat, radius, setColor)
  343. #endregion
  344. #region Constructor (forbidden)
  345. /// <summary>
  346. /// Geometry data
  347. /// </summary>
  348. private GeometryHelper()
  349. : base(null, null)
  350. {
  351. }
  352. #endregion
  353. }
  354. }