PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/ContentSystem/Rendering/GeometryData.cs

#
C# | 1805 lines | 1008 code | 155 blank | 642 comment | 63 complexity | 69a8189ae9f3a714d289af3d194777ae MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using Delta.ContentSystem.Rendering.Helpers;
  5. using Delta.Engine.Dynamic;
  6. using Delta.Utilities;
  7. using Delta.Utilities.Datatypes;
  8. using Delta.Utilities.Graphics;
  9. using Delta.Utilities.Helpers;
  10. using NUnit.Framework;
  11. namespace Delta.ContentSystem.Rendering
  12. {
  13. /// <summary>
  14. /// Geometry data class, just contains vertices and indices for rendering.
  15. /// Geometry meshes are highly dependant on the underlying type for the
  16. /// vertices. Some helper methods can be used to generate simple meshes
  17. /// (boxes, spheres, etc.). Data is stored in binary format for quick access
  18. /// and the used material data is also stored internally in here. Please note
  19. /// that rendering a level or a complex model usually consists of multiple
  20. /// meshes, a geometry mesh is just the lowest underlying 3D type in the
  21. /// engine! Meshes are even used for dynamic vertex buffers, lines and all 2D
  22. /// UI drawing, but those meshes are usually not saved out to content files.
  23. /// <para />
  24. /// While this is not directly a content type, it is pretty much what defines
  25. /// a mesh, which is used in many other content types as well (Models, Levels
  26. /// and also important for MeshAnimations, which are linked up in Models
  27. /// too). You can of course also use this class for your own dynamically
  28. /// created data (e.g. landscape rendering, BSP trees, or any other type of
  29. /// rendering)
  30. /// <para />
  31. /// Note: Geometry data can get quite big and while we need this class and
  32. /// all the internal data for dynamic geometry data that needs to be updated
  33. /// from time to time, it is not needed for static geometry data after the
  34. /// internal data structure has been created on the graphic card (vertex
  35. /// and index buffer objects). To save memory we can dispose this object
  36. /// and just in case it is needed again, we need to notify the owner to
  37. /// make sure that the data is reloaded again.
  38. /// </summary>
  39. /// <remarks>
  40. /// Vertex data. Note: Filling data into this is up to 5 times slower than
  41. /// directly filling a struct array. For setting this once or even loading
  42. /// from a file, this class is perfectly fine (loading from a file is even
  43. /// about 2 times faster than using struct arrays). However, for dynamic
  44. /// vertex buffers it is a good optimization to have a native struct
  45. /// representing the data and setting that instead with SetData (e.g. see
  46. /// the DrawManager implementation for line rendering using ColoredVertex).
  47. /// Please note that even with the slower speed of GeometryData it is still
  48. /// a very good idea to use for our engine because we want to use vertex
  49. /// and index buffers, which is only possible with a generic byte data
  50. /// stream and thus much faster as converting data types or not using
  51. /// vertex buffers. So rendering is as quick as it gets, especially for
  52. /// static geometry data (which is what we will use 99% of the time) and
  53. /// dynamic geometry is also rendered very quickly (in fact as fast as
  54. /// possible on each platform), just setting the data is a little slow on
  55. /// the CPU because of all the reader/writer stuff involved here.
  56. /// </remarks>
  57. public class GeometryData : ISaveLoadBinary, IDisposable
  58. {
  59. #region Constants
  60. /// <summary>
  61. /// Version number for this MeshData. If this goes above 1, we need
  62. /// to support loading older versions as well. Saving is always going
  63. /// to be the latest version (this one).
  64. /// </summary>
  65. private const int VersionNumber = 2;
  66. #endregion
  67. #region CreateCube (Static)
  68. /// <summary>
  69. /// Generate a cube mesh with the same size in all dimensions. Box meshes
  70. /// have their pivot point at Vector.Zero (to make them easier to place).
  71. /// Note: The GeometryHelper in Delta.Rendering.Models has many more
  72. /// Create methods to create spheres, cones, cylinders, etc.
  73. /// </summary>
  74. /// <param name="vertexFormat">The vertex format for this geometry</param>
  75. /// <param name="size">Size of the box in all dimensions</param>
  76. /// <param name="setColor">Vertex color during creation.</param>
  77. /// <returns>
  78. /// Geometry Data containing all vertices and indices for this box
  79. /// </returns>
  80. public static GeometryData CreateCube(VertexFormat vertexFormat,
  81. float size, Color setColor)
  82. {
  83. return CreateBox(vertexFormat, size, size, size, false, setColor);
  84. }
  85. /// <summary>
  86. /// Generate a cube mesh with the same size in all dimensions. Box meshes
  87. /// have their pivot point at Vector.Zero (to make them easier to place).
  88. /// Note: The GeometryHelper in Delta.Rendering.Models has many more
  89. /// Create methods to create spheres, cones, cylinders, etc.
  90. /// </summary>
  91. /// <param name="vertexFormat">The vertex format for this geometry</param>
  92. /// <param name="size">Size of the box in all dimensions</param>
  93. /// <returns>
  94. /// Geometry Data containing all vertices and indices for this box
  95. /// </returns>
  96. public static GeometryData CreateCube(VertexFormat vertexFormat, float size)
  97. {
  98. return CreateBox(vertexFormat, size, size, size, false, Color.White);
  99. }
  100. #endregion
  101. #region CreateBox (Static)
  102. /// <summary>
  103. /// Generates a box mesh with the given sizes and a material. Box meshes
  104. /// have their pivot point at Vector.Zero (to make them easier to place).
  105. /// </summary>
  106. /// <param name="vertexFormat">The vertex format.</param>
  107. /// <param name="width">The width in the X dimension</param>
  108. /// <param name="depth">The depth in the Y dimension</param>
  109. /// <param name="height">The height in the Z dimension</param>
  110. /// <param name="invertWinding">Whether to invert indices order</param>
  111. /// <param name="setColor">Vertex color during creation.</param>
  112. /// <returns>
  113. /// Geometry Data containing all vertices and indices for this box
  114. /// </returns>
  115. public static GeometryData CreateBox(VertexFormat vertexFormat,
  116. float width, float depth, float height, bool invertWinding, Color setColor)
  117. {
  118. return CreateBox(vertexFormat, width, depth, height, invertWinding,
  119. "<GeneratedBox(" + width + ", " + depth + ", " + height + ")>",
  120. setColor);
  121. }
  122. /// <summary>
  123. /// Generates a box mesh with a given pivot point height (usually used
  124. /// to generate the box at Vector.Zero, see other overloads).
  125. /// </summary>
  126. /// <param name="vertexFormat">The vertex format.</param>
  127. /// <param name="width">The width in the X dimension</param>
  128. /// <param name="depth">The depth in the Y dimension</param>
  129. /// <param name="height">The height in the Z dimension</param>
  130. /// <param name="invertWinding">Whether to invert indices order</param>
  131. /// <param name="meshName">Name of the mesh.</param>
  132. /// <param name="setColor">Vertex color during creation.</param>
  133. /// <returns>
  134. /// Geometry Data containing all vertices and indices for this box
  135. /// </returns>
  136. public static GeometryData CreateBox(VertexFormat vertexFormat,
  137. float width, float depth, float height, bool invertWinding,
  138. string meshName, Color setColor)
  139. {
  140. // The box has 8 positions, but we need 24 vertices (4 for each of the
  141. // 6 sides, each side has its own texture coordinates, see ordering)
  142. // Texture coordinates begin at the left upper corner (0, 0)
  143. GeometryData geometry = new GeometryData(
  144. meshName, 24, vertexFormat, 36, true, false);
  145. Vector cubeHalfSize = new Vector(width, height, depth) / 2;
  146. // Create each face in turn.
  147. int vertexIndex = 0;
  148. // front side
  149. geometry.SetVertexData(vertexIndex++,
  150. new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z),
  151. Point.UnitY, setColor, Vector.UnitZ, Vector.UnitZ);
  152. geometry.SetVertexData(vertexIndex++,
  153. new Vector(cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z),
  154. Point.One, setColor, Vector.UnitZ, Vector.UnitZ);
  155. geometry.SetVertexData(vertexIndex++,
  156. new Vector(cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z),
  157. Point.UnitX, setColor, Vector.UnitZ, Vector.UnitZ);
  158. geometry.SetVertexData(vertexIndex++,
  159. new Vector(-cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z),
  160. Point.Zero, setColor, Vector.UnitZ, Vector.UnitZ);
  161. // back side
  162. geometry.SetVertexData(vertexIndex++,
  163. new Vector(cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z),
  164. Point.UnitY, setColor, -Vector.UnitZ, -Vector.UnitZ);
  165. geometry.SetVertexData(vertexIndex++,
  166. new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z),
  167. Point.One, setColor, -Vector.UnitZ, -Vector.UnitZ);
  168. geometry.SetVertexData(vertexIndex++,
  169. new Vector(-cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z),
  170. Point.UnitX, setColor, -Vector.UnitZ, -Vector.UnitZ);
  171. geometry.SetVertexData(vertexIndex++,
  172. new Vector(cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z),
  173. Point.Zero, setColor, -Vector.UnitZ, -Vector.UnitZ);
  174. // left side
  175. geometry.SetVertexData(vertexIndex++,
  176. new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z),
  177. Point.UnitY, setColor, -Vector.UnitX, -Vector.UnitX);
  178. geometry.SetVertexData(vertexIndex++,
  179. new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z),
  180. Point.One, setColor, -Vector.UnitX, -Vector.UnitX);
  181. geometry.SetVertexData(vertexIndex++,
  182. new Vector(-cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z),
  183. Point.UnitX, setColor, -Vector.UnitX, -Vector.UnitX);
  184. geometry.SetVertexData(vertexIndex++,
  185. new Vector(-cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z),
  186. Point.Zero, setColor, -Vector.UnitX, -Vector.UnitX);
  187. // right side
  188. geometry.SetVertexData(vertexIndex++,
  189. new Vector(cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z),
  190. Point.UnitY, setColor, Vector.UnitX, Vector.UnitX);
  191. geometry.SetVertexData(vertexIndex++,
  192. new Vector(cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z),
  193. Point.One, setColor, Vector.UnitX, Vector.UnitX);
  194. geometry.SetVertexData(vertexIndex++,
  195. new Vector(cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z),
  196. Point.UnitX, setColor, Vector.UnitX, Vector.UnitX);
  197. geometry.SetVertexData(vertexIndex++,
  198. new Vector(cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z),
  199. Point.Zero, setColor, Vector.UnitX, Vector.UnitX);
  200. // up side
  201. geometry.SetVertexData(vertexIndex++,
  202. new Vector(-cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z),
  203. Point.UnitY, setColor, Vector.UnitY, Vector.UnitY);
  204. geometry.SetVertexData(vertexIndex++,
  205. new Vector(cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z),
  206. Point.One, setColor, Vector.UnitY, Vector.UnitY);
  207. geometry.SetVertexData(vertexIndex++,
  208. new Vector(cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z),
  209. Point.UnitX, setColor, Vector.UnitY, Vector.UnitY);
  210. geometry.SetVertexData(vertexIndex++,
  211. new Vector(-cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z),
  212. Point.Zero, setColor, Vector.UnitY, Vector.UnitY);
  213. // down side
  214. geometry.SetVertexData(vertexIndex++,
  215. new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z),
  216. Point.UnitY, setColor, -Vector.UnitY, -Vector.UnitY);
  217. geometry.SetVertexData(vertexIndex++,
  218. new Vector(cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z),
  219. Point.One, setColor, -Vector.UnitY, -Vector.UnitY);
  220. geometry.SetVertexData(vertexIndex++,
  221. new Vector(cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z),
  222. Point.UnitX, setColor, -Vector.UnitY, -Vector.UnitY);
  223. geometry.SetVertexData(vertexIndex++,
  224. new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z),
  225. Point.Zero, setColor, -Vector.UnitY, -Vector.UnitY);
  226. if (invertWinding)
  227. {
  228. geometry.Indices = new ushort[]
  229. {
  230. // front
  231. 2, 1, 0,
  232. 3, 2, 0,
  233. // back
  234. 6, 5, 4,
  235. 7, 6, 4,
  236. // left
  237. 10, 9, 8,
  238. 11, 10, 8,
  239. // right
  240. 14, 13, 12,
  241. 15, 14, 12,
  242. // up
  243. 18, 17, 16,
  244. 19, 18, 16,
  245. // down
  246. 22, 21, 20,
  247. 23, 22, 20
  248. };
  249. }
  250. else
  251. {
  252. geometry.Indices = new ushort[]
  253. {
  254. // front
  255. 0, 1, 2,
  256. 0, 2, 3,
  257. // back
  258. 4, 5, 6,
  259. 4, 6, 7,
  260. // left
  261. 8, 9, 10,
  262. 8, 10, 11,
  263. // right
  264. 12, 13, 14,
  265. 12, 14, 15,
  266. // up
  267. 16, 17, 18,
  268. 16, 18, 19,
  269. // down
  270. 20, 21, 22,
  271. 20, 22, 23
  272. };
  273. }
  274. return geometry;
  275. }
  276. #endregion
  277. #region CreateSphere (Static)
  278. /// <summary>
  279. /// Creates a sphere as an GeometryData object with the given parameters.
  280. /// </summary>
  281. /// <param name="vertexFormat">The vertex format to use.</param>
  282. /// <param name="radius">The radius for the sphere from the center</param>
  283. /// <param name="setColor">
  284. /// Color for the new sphere in case the vertex format supports colored
  285. /// vertices (otherwise ignored).
  286. /// </param>
  287. /// <param name="invertWinding">
  288. /// Whether to invert winding order (usually false, except you want to be
  289. /// inside the sphere).
  290. /// </param>
  291. /// <returns>The geometry data of the specified sphere.</returns>
  292. public static GeometryData CreateSphere(VertexFormat vertexFormat,
  293. float radius, Color setColor, bool invertWinding = false)
  294. {
  295. // Set up the subdivision in which we build the geometry of the sphere.
  296. // The tessellation indicates in how many segments the sphere is
  297. // interpolated, smaller spheres have less tessellation, bigger spheres
  298. // use more tessellation, but we keep it between 4 and 32.
  299. int tessellation = (int)(MathHelper.Sqrt(radius) * 6.0f);
  300. tessellation = MathHelper.Clamp(tessellation, 6, 32);
  301. // Make multiple of 3 for better fitting texturing
  302. tessellation = (tessellation / 3) * 3;
  303. int verticalSegments = tessellation;
  304. // Add one horizontal segment to fit around a circle, good for UVs
  305. int horizontalSegments = tessellation * 2 + 1;
  306. // Create rings of vertices at progressively higher latitudes.
  307. GeometryData geometry = new GeometryData(
  308. "<GeneratedSphere(" + radius + ")>",
  309. verticalSegments * horizontalSegments,
  310. vertexFormat, 6 * (verticalSegments - 1) * horizontalSegments,
  311. true, false);
  312. for (int index = 0; index < verticalSegments; index++)
  313. {
  314. // Lets begin with the latitude and divide each segment. As we are
  315. // using circular surface, we will split each position along the
  316. // vertical axis with the cosine. That is every position in the
  317. // vertical segment creates a ring with a maximum width stated by the
  318. // cosine (at the top width=0, in the medium reaches the maximum
  319. // width = 1, and at the bottom width=0).
  320. float latitude = index * 180.0f / (verticalSegments - 1) - 90.0f;
  321. float dy = MathHelper.Sin(latitude);
  322. float dxz = MathHelper.Cos(latitude);
  323. // Create a single ring of vertices at this latitude.
  324. for (int j = 0; j < horizontalSegments; j++)
  325. {
  326. // Next step is tessellation along horizontal axis in which we just
  327. // simple indicates the position of each vertex in the ring with the
  328. // previously established width along the surface of the sphere
  329. float longitude = j * 360.0f / (horizontalSegments - 1);
  330. float dx = MathHelper.Cos(longitude) * dxz;
  331. float dz = MathHelper.Sin(longitude) * dxz;
  332. // finally we got the correct position
  333. Vector normal = new Vector(dx, dy, dz);
  334. // and we assign the corresponding U,V coordinate of the texture
  335. // in a way that for each circle of the sphere it contains a line
  336. // of the texture image
  337. geometry.SetVertexData(
  338. index * horizontalSegments + j,
  339. normal * radius,
  340. new Point(j / (float)(horizontalSegments - 1),
  341. index / (float)(verticalSegments - 1)), setColor, normal,
  342. Vector.Cross(normal, Vector.UnitZ));
  343. }
  344. }
  345. // Create a fan connecting the bottom vertex to the bottom latitude ring
  346. // and finally set up the indices connecting each vertex.
  347. // Fill the sphere body with triangles joining each pair of rings.
  348. int num = 0;
  349. for (int index = 0; index < verticalSegments - 1; index++)
  350. {
  351. for (int j = 0; j < horizontalSegments; j++)
  352. {
  353. int nextI = (index + 1);
  354. int nextJ = (j + 1) % horizontalSegments;
  355. if (invertWinding)
  356. {
  357. geometry.Indices[num++] =
  358. (ushort)(index * horizontalSegments + nextJ);
  359. geometry.Indices[num++] =
  360. (ushort)(nextI * horizontalSegments + j);
  361. geometry.Indices[num++] =
  362. (ushort)(index * horizontalSegments + j);
  363. geometry.Indices[num++] =
  364. (ushort)(nextI * horizontalSegments + nextJ);
  365. geometry.Indices[num++] =
  366. (ushort)(nextI * horizontalSegments + j);
  367. geometry.Indices[num++] =
  368. (ushort)(index * horizontalSegments + nextJ);
  369. }
  370. else
  371. {
  372. geometry.Indices[num++] =
  373. (ushort)(index * horizontalSegments + j);
  374. geometry.Indices[num++] =
  375. (ushort)(nextI * horizontalSegments + j);
  376. geometry.Indices[num++] =
  377. (ushort)(index * horizontalSegments + nextJ);
  378. geometry.Indices[num++] =
  379. (ushort)(index * horizontalSegments + nextJ);
  380. geometry.Indices[num++] =
  381. (ushort)(nextI * horizontalSegments + j);
  382. geometry.Indices[num++] =
  383. (ushort)(nextI * horizontalSegments + nextJ);
  384. }
  385. }
  386. }
  387. return geometry;
  388. }
  389. #endregion
  390. #region CreatePlane (Static)
  391. /// <summary>
  392. /// Create plane
  393. /// </summary>
  394. /// <param name="vertexFormat">The vertex format.</param>
  395. /// <param name="setWidth">Width of the set.</param>
  396. /// <param name="setHeight">Height of the set.</param>
  397. /// <returns>The geometry data of the specified plane.</returns>
  398. public static GeometryData CreatePlane(VertexFormat vertexFormat,
  399. float setWidth, float setHeight)
  400. {
  401. return CreatePlane(vertexFormat, setWidth, setHeight, Color.White);
  402. }
  403. /// <summary>
  404. /// Creates an XY plane in the specified vertex format (you can use
  405. /// one of the predefined vertex formats from VertexData).
  406. /// </summary>
  407. /// <param name="vertexFormat">The vertex format.</param>
  408. /// <param name="setWidth">Set width.</param>
  409. /// <param name="setHeight">Set Height.</param>
  410. /// <param name="setColor">Set Color.</param>
  411. /// <returns>The geometry data of the specified plane.</returns>
  412. public static GeometryData CreatePlane(VertexFormat vertexFormat,
  413. float setWidth, float setHeight, Color setColor)
  414. {
  415. float halfWidth = setWidth * 0.5f;
  416. float halfDepth = setHeight * 0.5f;
  417. float height = 0;
  418. // We use 4 vertices with 6 indices (0, 1, 2, 2, 1, 3):
  419. // 0 - 1
  420. // | / |
  421. // 2 - 3
  422. GeometryData geometry = new GeometryData(
  423. "<GeneratedPlane(" + setWidth + "x" + setHeight + ")>",
  424. 4, vertexFormat, 6, true, false);
  425. Vector planeNormal = new Vector(0, 0, 1);
  426. Vector planeTangent = new Vector(1, 0, 0);
  427. // Front left
  428. geometry.SetVertexData(0,
  429. new Vector(-halfWidth, halfDepth, height),
  430. new Point(0, 0), setColor, planeNormal, planeTangent);
  431. // Front right
  432. geometry.SetVertexData(1,
  433. new Vector(halfWidth, halfDepth, height),
  434. new Point(1, 0), setColor, planeNormal, planeTangent);
  435. // Rear left
  436. geometry.SetVertexData(2,
  437. new Vector(-halfWidth, -halfDepth, height),
  438. new Point(0, 1), setColor, planeNormal, planeTangent);
  439. // Rear right
  440. geometry.SetVertexData(3,
  441. new Vector(halfWidth, -halfDepth, height),
  442. new Point(1, 1), setColor, planeNormal, planeTangent);
  443. // Top left polygon (Note: Counter-Clockwise is front)
  444. geometry.Indices[0] = 0;
  445. geometry.Indices[1] = 2;
  446. geometry.Indices[2] = 1;
  447. // Bottom right polygon
  448. geometry.Indices[3] = 2;
  449. geometry.Indices[4] = 3;
  450. geometry.Indices[5] = 1;
  451. return geometry;
  452. }
  453. #endregion
  454. #region CreateSegmentedPlane (Static)
  455. /// <summary>
  456. /// Creates an XY plane in the specified vertex format (you can use
  457. /// one of the predefined vertex formats from VertexData).
  458. /// </summary>
  459. /// <param name="vertexFormat">The vertex format.</param>
  460. /// <param name="setWidth">Sets Width.</param>
  461. /// <param name="setHeight">Sets Height.</param>
  462. /// <param name="segments">The segments.</param>
  463. /// <param name="uvForEachSegment">Create new uv from 0-1 for each segment.
  464. /// Useful for tiling (works even without tileable textures)</param>
  465. /// <param name="setColor">Sets Color.</param>
  466. public static GeometryData CreateSegmentedPlane(VertexFormat vertexFormat,
  467. float setWidth, float setHeight, int segments, bool uvForEachSegment,
  468. Color setColor)
  469. {
  470. if (segments < 1)
  471. {
  472. throw new InvalidOperationException(
  473. "You need at least one segment for CreateSegmentedPlane!");
  474. }
  475. float halfWidth = setWidth * 0.5f;
  476. float halfDepth = setHeight * 0.5f;
  477. float height = 0;
  478. // We use 4 vertices with 6 indices (0, 1, 2, 2, 1, 3):
  479. // 0 - 1
  480. // | / |
  481. // 2 - 3
  482. // But duplicate this many times for as many segments in each direction.
  483. int totalSegments = segments * segments;
  484. int segmentStride = segments + 1;
  485. int totalPoints =
  486. uvForEachSegment
  487. ? totalSegments * 4
  488. : segmentStride * segmentStride;
  489. GeometryData geometry = new GeometryData(
  490. "<GeneratedSegmentedPlane(" + setWidth + "x" + setHeight + ")>",
  491. totalPoints, vertexFormat, 6 * totalSegments, true, false);
  492. Vector planeNormal = new Vector(0, 0, 1);
  493. Vector planeTangent = new Vector(1, 0, 0);
  494. // For each segment + 1, build the points
  495. if (uvForEachSegment)
  496. {
  497. // For each segment, build up the 2 triangles
  498. int index = 0;
  499. int pointIndex = 0;
  500. for (int ySegment = 0; ySegment < segments; ySegment++)
  501. {
  502. for (int xSegment = 0; xSegment < segments; xSegment++)
  503. {
  504. float xFactor = xSegment / (float)segments;
  505. float yFactor = ySegment / (float)segments;
  506. float xFactor2 = (xSegment + 1) / (float)segments;
  507. float yFactor2 = (ySegment + 1) / (float)segments;
  508. geometry.SetVertexData(pointIndex + 0,
  509. new Vector(-halfWidth + xFactor * setWidth,
  510. halfDepth - yFactor * setHeight, height),
  511. new Point(0, 0), setColor, planeNormal, planeTangent);
  512. geometry.SetVertexData(pointIndex + 1,
  513. new Vector(-halfWidth + xFactor2 * setWidth,
  514. halfDepth - yFactor * setHeight, height),
  515. new Point(1, 0), setColor, planeNormal, planeTangent);
  516. geometry.SetVertexData(pointIndex + 2,
  517. new Vector(-halfWidth + xFactor * setWidth,
  518. halfDepth - yFactor2 * setHeight, height),
  519. new Point(0, 1), setColor, planeNormal, planeTangent);
  520. geometry.SetVertexData(pointIndex + 3,
  521. new Vector(-halfWidth + xFactor2 * setWidth,
  522. halfDepth - yFactor2 * setHeight, height),
  523. new Point(1, 1), setColor, planeNormal, planeTangent);
  524. // Top left polygon (Note: Counter-Clockwise is front)
  525. geometry.Indices[index++] = //0,0
  526. (ushort)(pointIndex + 0);
  527. geometry.Indices[index++] = //0,1
  528. (ushort)(pointIndex + 2);
  529. geometry.Indices[index++] = //1,0
  530. (ushort)(pointIndex + 1);
  531. // Bottom right polygon
  532. geometry.Indices[index++] = //0,1
  533. (ushort)(pointIndex + 2);
  534. geometry.Indices[index++] = //1,1
  535. (ushort)(pointIndex + 3);
  536. geometry.Indices[index++] = //1,0
  537. (ushort)(pointIndex + 1);
  538. pointIndex += 4;
  539. }
  540. }
  541. }
  542. else
  543. {
  544. int pointIndex = 0;
  545. for (int ySegment = 0; ySegment < segments + 1; ySegment++)
  546. {
  547. for (int xSegment = 0; xSegment < segments + 1; xSegment++)
  548. {
  549. float xFactor = xSegment / (float)segments;
  550. float yFactor = ySegment / (float)segments;
  551. geometry.SetVertexData(pointIndex++,
  552. new Vector(-halfWidth + xFactor * setWidth,
  553. halfDepth - yFactor * setHeight, height),
  554. new Point(xFactor, yFactor),
  555. setColor, planeNormal, planeTangent);
  556. }
  557. }
  558. // For each segment, build up the 2 triangles
  559. int index = 0;
  560. for (int ySegment = 0; ySegment < segments; ySegment++)
  561. {
  562. for (int xSegment = 0; xSegment < segments; xSegment++)
  563. {
  564. // Top left polygon (Note: Counter-Clockwise is front)
  565. geometry.Indices[index++] = //0,0
  566. (ushort)(xSegment + 0 + (ySegment + 0) * segmentStride);
  567. geometry.Indices[index++] = //0,1
  568. (ushort)(xSegment + 0 + (ySegment + 1) * segmentStride);
  569. geometry.Indices[index++] = //1,0
  570. (ushort)(xSegment + 1 + (ySegment + 0) * segmentStride);
  571. // Bottom right polygon
  572. geometry.Indices[index++] = //0,1
  573. (ushort)(xSegment + 0 + (ySegment + 1) * segmentStride);
  574. geometry.Indices[index++] = //1,1
  575. (ushort)(xSegment + 1 + (ySegment + 1) * segmentStride);
  576. geometry.Indices[index++] = //1,0
  577. (ushort)(xSegment + 1 + (ySegment + 0) * segmentStride);
  578. }
  579. }
  580. }
  581. return geometry;
  582. }
  583. /// <summary>
  584. /// Create segmented plane.
  585. /// </summary>
  586. /// <param name="vertexFormat">The vertex format.</param>
  587. /// <param name="setWidth">Width of the set.</param>
  588. /// <param name="setHeight">Height of the set.</param>
  589. /// <param name="segments">The segments.</param>
  590. /// <returns></returns>
  591. public static GeometryData CreateSegmentedPlane(VertexFormat vertexFormat,
  592. float setWidth, float setHeight, int segments)
  593. {
  594. return CreateSegmentedPlane(vertexFormat, setWidth, setHeight, segments,
  595. false, Color.White);
  596. }
  597. #endregion
  598. #region CreateCapsule (Static)
  599. /// <summary>
  600. /// Creates a capsule geometry in the specified vertex format (you can use
  601. /// one of the predefined vertex formats from VertexData).
  602. /// </summary>
  603. /// <param name="vertexFormat">The vertex format.</param>
  604. /// <param name="setDiameter">Diameter of the capsule</param>
  605. /// <param name="setLength">Length of the capsule</param>
  606. /// <returns>The geometry data of the specified capsule.</returns>
  607. public static GeometryData CreateCapsule(VertexFormat vertexFormat,
  608. float setDiameter, float setLength)
  609. {
  610. return CreateCapsule(vertexFormat, setDiameter, setLength,
  611. Color.White);
  612. }
  613. /// <summary>
  614. /// Creates a capsule geometry in the specified vertex format (you can use
  615. /// one of the predefined vertex formats from VertexData).
  616. /// </summary>
  617. /// <param name="vertexFormat">The vertex format.</param>
  618. /// <param name="setDiameter">Diameter of the capsule</param>
  619. /// <param name="setLength">Length of the capsule</param>
  620. /// <param name="setColor">Color for the capsule</param>
  621. /// <returns>
  622. /// Geometry Data with the capsule, using tessellation of 12.
  623. /// </returns>
  624. public static GeometryData CreateCapsule(VertexFormat vertexFormat,
  625. float setDiameter, float setLength, Color setColor)
  626. {
  627. int tessellation = 12;
  628. int verticalSegments = tessellation;
  629. int horizontalSegments = tessellation * 2;
  630. float radius = setDiameter / 2;
  631. int vertexCount = horizontalSegments * (verticalSegments - 1) + 2;
  632. GeometryData geometry = new GeometryData(
  633. "<GeneratedSegmentedPlane(" + setDiameter + "x" + setLength + ")>",
  634. vertexCount, vertexFormat, 6 * vertexCount, true, false);
  635. Vector down = -Vector.UnitZ;
  636. // For each segment + 1, build the points
  637. int pointIndex = 0;
  638. // Start with a single vertex at the bottom of the sphere.
  639. geometry.SetVertexData(pointIndex++,
  640. down * radius + down * 0.5f * setLength,
  641. Point.Zero, setColor, down, down);
  642. // Create rings of vertices at progressively higher latitudes.
  643. for (int i = 0; i < verticalSegments - 1; i++)
  644. {
  645. float latitude = ((i + 1) * MathHelper.Pi /
  646. verticalSegments) - MathHelper.PiHalf;
  647. float dy = (float)Math.Sin(latitude);
  648. float dxz = (float)Math.Cos(latitude);
  649. bool bla = false;
  650. if (i > (verticalSegments - 2) / 2)
  651. {
  652. bla = true;
  653. }
  654. // Create a single ring of vertices at this latitude.
  655. for (int j = 0; j < horizontalSegments; j++)
  656. {
  657. float longitude = j * MathHelper.PiDouble / horizontalSegments;
  658. float dx = (float)Math.Cos(longitude) * dxz;
  659. float dz = (float)Math.Sin(longitude) * dxz;
  660. Vector normal = new Vector(dx, dy, dz);
  661. Vector position = normal * radius;
  662. if (bla)
  663. {
  664. position += Vector.UnitZ * 0.5f * setLength;
  665. }
  666. else
  667. {
  668. position += down * 0.5f * setLength;
  669. }
  670. geometry.SetVertexData(pointIndex++,
  671. position,
  672. new Point(1.0f, 0.0f), setColor, normal, normal);
  673. }
  674. }
  675. // Finish with a single vertex at the top of the sphere.
  676. geometry.SetVertexData(pointIndex++,
  677. Vector.UnitZ * radius + Vector.UnitZ * 0.5f * setLength,
  678. new Point(0.0f, 1.0f), setColor, Vector.UnitZ, Vector.UnitZ);
  679. int index = 0;
  680. // Create a fan connecting the bottom vertex to the bottom latitude ring.
  681. for (int i = 0; i < horizontalSegments; i++)
  682. {
  683. geometry.Indices[index++] = (ushort)(1 + i);
  684. geometry.Indices[index++] = (ushort)(1 + (i + 1) % horizontalSegments);
  685. geometry.Indices[index++] = 0;
  686. }
  687. // Fill the sphere body with triangles joining each pair of latitude rings.
  688. for (int i = 0; i < verticalSegments - 2; i++)
  689. {
  690. for (int j = 0; j < horizontalSegments; j++)
  691. {
  692. int nextI = i + 1;
  693. int nextJ = (j + 1) % horizontalSegments;
  694. geometry.Indices[index++] =
  695. (ushort)(1 + nextI * horizontalSegments + j);
  696. geometry.Indices[index++] =
  697. (ushort)(1 + i * horizontalSegments + nextJ);
  698. geometry.Indices[index++] =
  699. (ushort)(1 + i * horizontalSegments + j);
  700. geometry.Indices[index++] =
  701. (ushort)(1 + nextI * horizontalSegments + j);
  702. geometry.Indices[index++] =
  703. (ushort)(1 + nextI * horizontalSegments + nextJ);
  704. geometry.Indices[index++] =
  705. (ushort)(1 + i * horizontalSegments + nextJ);
  706. }
  707. }
  708. // Create a fan connecting the top vertex to the top latitude ring.
  709. for (int i = 0; i < horizontalSegments; i++)
  710. {
  711. geometry.Indices[index++] =
  712. (ushort)(vertexCount - 2 - i);
  713. geometry.Indices[index++] =
  714. (ushort)(vertexCount - 2 - (i + 1) % horizontalSegments);
  715. geometry.Indices[index++] =
  716. (ushort)(vertexCount - 1);
  717. }
  718. return geometry;
  719. }
  720. #endregion
  721. #region Create (Static)
  722. /// <summary>
  723. /// Helper method to create a geometry out of position vectors, indices
  724. /// and a color for each position. Will use VertexFormat.Position3DColor.
  725. /// </summary>
  726. /// <param name="positions">3D positions for the new geometry.</param>
  727. /// <param name="colors">
  728. /// Colors for each of the positions, must match the length of the
  729. /// positions list.
  730. /// </param>
  731. /// <param name="indices">
  732. /// Indices to connect the triangles, must be at least 3 for one triangle
  733. /// or a multiple of 3 for more.
  734. /// </param>
  735. /// <returns>
  736. /// A newly created GeometryData, which can be passed to MeshData to be
  737. /// used for rendering.
  738. /// </returns>
  739. public static GeometryData Create(List<Vector> positions,
  740. List<Color> colors, List<ushort> indices)
  741. {
  742. if (colors.Count != positions.Count)
  743. {
  744. throw new ArgumentException(
  745. "The colors list length=" + colors.Count + " must match the " +
  746. "positions list length=" + positions.Count);
  747. }
  748. if (indices.Count < 3 ||
  749. indices.Count % 3 != 0)
  750. {
  751. throw new ArgumentException(
  752. "The indices list length=" + indices.Count + " must be at least 3 " +
  753. "for one triangle or a multiple of 3 for more.");
  754. }
  755. // And finally create the geometry out of the custom positions
  756. GeometryData geometry = new GeometryData(
  757. "<CustomColoredGeometry>", positions.Count,
  758. VertexFormat.Position3DColor, indices.Count, true, false);
  759. // Add all vertices from the positions
  760. for (int num = 0; num < positions.Count; num++)
  761. {
  762. // Note: Only position and color is used here, rest is ignored.
  763. geometry.SetVertexData(num, positions[num], Point.Zero, colors[num],
  764. Vector.UnitZ, Vector.UnitZ);
  765. }
  766. // Set the indices
  767. geometry.Indices = indices.ToArray();
  768. return geometry;
  769. }
  770. #endregion
  771. #region Create (Static)
  772. /// <summary>
  773. /// Helper method to create a geometry out of position vectors, indices
  774. /// and a uv for each position. Will use VertexFormat.Position3DTextured.
  775. /// </summary>
  776. /// <param name="positions">3D positions for the new geometry.</param>
  777. /// <param name="uvs">
  778. /// Texture uvs for each of the positions, must match the length of the
  779. /// positions list.
  780. /// </param>
  781. /// <param name="indices">
  782. /// Indices to connect the triangles, must be at least 3 for one triangle
  783. /// or a multiple of 3 for more.
  784. /// </param>
  785. /// <returns>
  786. /// A newly created GeometryData, which can be passed to MeshData to be
  787. /// used for rendering.
  788. /// </returns>
  789. public static GeometryData Create(List<Vector> positions, List<Point> uvs,
  790. List<ushort> indices)
  791. {
  792. if (uvs.Count != positions.Count)
  793. {
  794. throw new ArgumentException(
  795. "The uvs list length=" + uvs.Count + " must match the " +
  796. "positions list length=" + positions.Count);
  797. }
  798. if (indices.Count < 3 ||
  799. indices.Count % 3 != 0)
  800. {
  801. throw new ArgumentException(
  802. "The indices list length=" + indices.Count + " must be at least 3 " +
  803. "for one triangle or a multiple of 3 for more.");
  804. }
  805. // And finally create the geometry out of the custom positions
  806. GeometryData geometry = new GeometryData(
  807. "<CustomColoredGeometry>", positions.Count,
  808. VertexFormat.Position3DTextured, indices.Count, true, false);
  809. // Add all vertices from the positions
  810. for (int num = 0; num < positions.Count; num++)
  811. {
  812. // Note: Only position and uv is used here, rest is ignored.
  813. geometry.SetVertexData(num, positions[num], uvs[num], Color.White,
  814. Vector.UnitZ, Vector.UnitZ);
  815. }
  816. // Set the indices
  817. geometry.Indices = indices.ToArray();
  818. return geometry;
  819. }
  820. #endregion
  821. #region DefaultBox (Static)
  822. /// <summary>
  823. /// Default box (created on demand) with the size (1, 1). Usually using
  824. /// the default material and used to display fallback content if loading
  825. /// failed (e.g. if mesh content file was not found).
  826. /// </summary>
  827. public static GeometryData DefaultBox
  828. {
  829. get
  830. {
  831. if (defaultBoxMesh == null)
  832. {
  833. defaultBoxMesh = CreateCube(VertexFormat.Position3DTextured, 1);
  834. }
  835. return defaultBoxMesh;
  836. }
  837. }
  838. #endregion
  839. #region Name (Public)
  840. /// <summary>
  841. /// Name of the geometry, useful for debugging or identify warnings.
  842. /// </summary>
  843. public string Name
  844. {
  845. get;
  846. private set;
  847. }
  848. #endregion
  849. #region writer (Public)
  850. /// <summary>
  851. /// Writer to directly manipulate the vertex data (use Seek and Write).
  852. /// You can also just define or use an existing vertex struct and use
  853. /// SetData to store it into this GeometryData.
  854. /// </summary>
  855. public BinaryWriter writer;
  856. #endregion
  857. #region reader (Public)
  858. /// <summary>
  859. /// Reader to get data back. For debugging you could also use the Get
  860. /// methods defined below (GetPosition, etc.), but they are slower.
  861. /// </summary>
  862. public BinaryReader reader;
  863. #endregion
  864. #region MaxNumberOfVertices (Public)
  865. /// <summary>
  866. /// Number of vertices this VertexData currently can hold. We usually only
  867. /// increase this if the buffer needs to get bigger (e.g. via SetData or
  868. /// IncreaseVertices). Normally this should be set once in the constructor
  869. /// however (for static meshes we know beforehand)! This value is usually
  870. /// the same as NumberOfUsedVertices, but for dynamic vertex data (e.g.
  871. /// in DrawManager for lines or in MaterialManager for 2D UI vertices)
  872. /// the number of used vertices can be lower and increase over time!
  873. /// </summary>
  874. public int MaxNumberOfVertices
  875. {
  876. get;
  877. private set;
  878. }
  879. #endregion
  880. #region NumberOfUsedVertices (Public)
  881. /// <summary>
  882. /// Number of used vertices, must always be the same (for static meshes)
  883. /// or lower than MaxNumberOfVertices. For dynamic vertex data this can
  884. /// go up to more than MaxNumberOfVertices can handle, then just call
  885. /// IncreaseMaxVertices to increase MaxNumberOfVertices!
  886. /// </summary>
  887. public int NumberOfUsedVertices
  888. {
  889. get
  890. {
  891. return numberOfUsedVerticesInternal;
  892. }
  893. }
  894. #endregion
  895. #region Format (Public)
  896. /// <summary>
  897. /// Vertex element format as specified in the constructor. Everything
  898. /// else here depends on it, so changing this is not possible, create
  899. /// a new VertexData instance to convert vertex formats.
  900. /// </summary>
  901. public VertexFormat Format
  902. {
  903. get;
  904. private set;
  905. }
  906. #endregion
  907. #region VertexDataLengthInBytes (Public)
  908. /// <summary>
  909. /// Vertex data length for each of the vertices in bytes.
  910. /// </summary>
  911. public int VertexDataLengthInBytes
  912. {
  913. get;
  914. private set;
  915. }
  916. #endregion
  917. #region Indices (Public)
  918. /// <summary>
  919. /// And all the indices that link into the vertices. For 2D, UI or
  920. /// effect billboards these indices can be pre-calculated (e.g.
  921. /// 0, 1, 2, 0, 2, 3) and be used with whatever vertices are used.
  922. /// </summary>
  923. public ushort[] Indices;
  924. #endregion
  925. #region NumberOfUsedIndices (Public)
  926. /// <summary>
  927. /// Number of used indices, only used for dynamic vertex and index data.
  928. /// Note: Not in Indices because that struct only contains the indices
  929. /// arrays as unions.
  930. /// </summary>
  931. public int NumberOfUsedIndices;
  932. #endregion
  933. #region Bones (Public)
  934. /// <summary>
  935. /// Flat list of bones, the first bone is always the root bone, all
  936. /// children can be accessed from here. The main reason for having a flat
  937. /// list is easy access to all bones for showing bone previous and of
  938. /// course to quickly access all animation matrices.
  939. /// </summary>
  940. public BoneData[] Bones = new BoneData[0];
  941. #endregion
  942. #region IsStatic (Public)
  943. /// <summary>
  944. /// Is the data in this Mesh static or dynamic? Only dynamic data can
  945. /// be changed at runtime and will force a dynamic vertex buffer update.
  946. /// Always true for loaded content, sometimes false for generated content.
  947. /// </summary>
  948. public bool IsStatic
  949. {
  950. get;
  951. private set;
  952. }
  953. #endregion
  954. #region IsStaticAndLoadedFromContent (Public)
  955. /// <summary>
  956. /// For static geometry we can get rid of the vertices and indices as we
  957. /// have them now on the GPU. This saves memory and the content can be
  958. /// reloaded at any time too.
  959. /// </summary>
  960. public bool IsStaticAndLoadedFromContent
  961. {
  962. get
  963. {
  964. return IsStatic &&
  965. reloadContent != null;
  966. }
  967. }
  968. #endregion
  969. #region HasDataChanged (Public)
  970. /// <summary>
  971. /// This value is used to check if the data has changed for dynamic
  972. /// geometry objects. Only if this is true we actually need to update the
  973. /// native objects on the GPU (VBO and IBO), which is slow and should be
  974. /// avoided if nothing has changed (e.g. no new lines were added).
  975. /// </summary>
  976. public bool HasDataChanged = true;
  977. #endregion
  978. #region IsLineData (Public)
  979. /// <summary>
  980. /// Is line data in the vertex stream? Only used for dynamic mesh data
  981. /// for the DrawManager. Only used without indices.
  982. /// </summary>
  983. public bool IsLineData
  984. {
  985. get;
  986. private set;
  987. }
  988. #endregion
  989. #region IsSky (Public)
  990. /// <summary>
  991. /// Is sky
  992. /// </summary>
  993. public bool IsSky
  994. {
  995. get;
  996. private set;
  997. }
  998. #endregion
  999. #region PolygonCount (Public)
  1000. /// <summary>
  1001. /// Gets the number of polygons for this mesh (indices / 3)
  1002. /// </summary>
  1003. public int PolygonCount
  1004. {
  1005. get
  1006. {
  1007. return Indices.Length / 3;
  1008. }
  1009. }
  1010. #endregion
  1011. #region BoundingBox (Public)
  1012. /// <summary>
  1013. /// Get bounding box of all vertices (in whatever space they are). Only
  1014. /// calculated once and also used for the BoundingSphere calculation.
  1015. /// </summary>
  1016. public BoundingBox BoundingBox
  1017. {
  1018. get
  1019. {
  1020. // Was this bounding box initialized before? Note: Normally it is
  1021. // already initialized at creation time for all processed content (from
  1022. // the content-pipeline), only code-generated won't be initialized.
  1023. if (IsStatic &&
  1024. (boundingBox.Max.X != 0 ||
  1025. boundingBox.Max.Y != 0 ||
  1026. boundingBox.Max.Z != 0))
  1027. {
  1028. // Then just return it.
  1029. return boundingBox;
  1030. }
  1031. // Else just get all vertex positions to compute it
  1032. Vector[] positions = new Vector[numberOfUsedVerticesInternal];
  1033. for (int index = 0; index < numberOfUsedVerticesInternal; index++)
  1034. {
  1035. positions[index] = GetPosition(index);
  1036. }
  1037. // before we return it
  1038. boundingBox = BoundingBox.Create(positions);
  1039. return boundingBox;
  1040. }
  1041. }
  1042. #endregion
  1043. #region BoundingSphere (Public)
  1044. /// <summary>
  1045. /// Bounding radius
  1046. /// </summary>
  1047. public BoundingSphere BoundingSphere
  1048. {
  1049. get
  1050. {
  1051. // Only calculate bounding radius once.
  1052. if (IsStatic == false ||
  1053. sphere.Radius == 0.0f)
  1054. {
  1055. sphere = BoundingBox.ToBoundingSphere();
  1056. }
  1057. return sphere;
  1058. }
  1059. }
  1060. #endregion
  1061. #region CheckFirstPlaneId (Public)
  1062. /// <summary>
  1063. /// The ID of the plane that the box was outside last in the
  1064. /// frustum check. Most of the time the frustum check will fail at the
  1065. /// same plane again so checking that one first makes it faster.
  1066. /// </summary>
  1067. public byte CheckFirstPlaneId
  1068. {
  1069. get;
  1070. set;
  1071. }
  1072. #endregion
  1073. #region Private
  1074. #region defaultBoxMesh (Private)
  1075. /// <summary>
  1076. /// Cached default box mesh data.
  1077. /// </summary>
  1078. private static GeometryData defaultBoxMesh;
  1079. #endregion
  1080. #region numberOfUsedVerticesInternal (Private)
  1081. /// <summary>
  1082. /// Optimization to make the NumberOfUsedVertices property as fast as
  1083. /// possible, especially in debug mode and when profiling the property
  1084. /// set slows us down.
  1085. /// </summary>
  1086. private int numberOfUsedVerticesInternal;
  1087. #endregion
  1088. #region boundingBox (Private)
  1089. /// <summary>
  1090. /// Box
  1091. /// </summary>
  1092. private BoundingBox boundingBox;
  1093. #endregion
  1094. #region sphere (Private)
  1095. /// <summary>
  1096. /// Sphere
  1097. /// </summary>
  1098. private BoundingSphere sphere = new BoundingSphere(Vector.Zero, 0.0f);
  1099. #endregion
  1100. #region stream (Private)
  1101. /// <summary>
  1102. /// Data stream for all the data.
  1103. /// </summary>
  1104. private MemoryStream stream;
  1105. #endregion
  1106. #region reloadContent (Private)
  1107. /// <summary>
  1108. /// Helper delegate to reload this geometry content after it has been
  1109. /// disposed. Usually used for static geometry, which can be disposed
  1110. /// after the GPU VBO and IBO have been created to save memory.
  1111. /// </summary>
  1112. private readonly RunDelegate reloadContent;
  1113. #endregion
  1114. #endregion
  1115. #region Constructors
  1116. /// <summary>
  1117. /// Create geometry data with a predefined number of vertices. This way all
  1118. /// the vertex data is already allocated and future writes to this vertex
  1119. /// data are fast. Use the writer directly to write up to 2000 vertices,
  1120. /// for more use the SetData method below.
  1121. /// Note: When rendering this is always send to the native vertex buffer
  1122. /// (e.g. in Xna DynamicVertexBuffer is used). The index buffer is not used
  1123. /// if setNumberOfIndices is 0, else you must fill the indices yourself
  1124. /// after filling in the vertices.
  1125. /// </summary>
  1126. /// <param name="setName">
  1127. /// Set name for this geometry for identification. Should start with &gt;
  1128. /// for dynamically created geometry to not confuse it with loaded gometry
  1129. /// from content, which uses content names.
  1130. /// </param>
  1131. /// <param name="setMaxNumberOfVertices">
  1132. /// Set maximum number of vertices to be used, can be increased if the
  1133. /// geometry is dynamic (not static).
  1134. /// </param>
  1135. /// <param name="setVertexFormat">
  1136. /// Set vertex format (usually from a shader).
  1137. /// </param>
  1138. /// <param name="setNumberOfIndices">Set number of indices</param>
  1139. /// <param name="setIsStaticData">
  1140. /// Is static data? Faster to render because does not need to be modified.
  1141. /// </param>
  1142. /// <param name="setIsLineData">
  1143. /// Set if line data is used in this vertex stream. Only used for dynamic
  1144. /// mesh data for the DrawManager. Only used without indices.
  1145. /// </param>
  1146. public GeometryData(string setName, int setMaxNumberOfVertices,
  1147. VertexFormat setVertexFormat, int setNumberOfIndices,
  1148. bool setIsStaticData, bool setIsLineData)
  1149. : this(setName, setMaxNumberOfVertices, setVertexFormat,
  1150. setNumberOfIndices, setIsStaticData, setIsLineData,
  1151. new BoundingBox(Vector.Zero, Vector.Zero))
  1152. {
  1153. }
  1154. /// <summary>
  1155. /// Create geometry data with a predefined number of vertices. This way all
  1156. /// the vertex data is already allocated and future writes to this vertex
  1157. /// data are fast. Use the writer directly to write up to 2000 vertices,
  1158. /// for more use the SetData method below.
  1159. /// Note: When rendering this is always send to the native vertex buffer
  1160. /// (e.g. in Xna DynamicVertexBuffer is used). The index buffer is not used
  1161. /// if setNumberOfIndices is 0, else you must fill the indices yourself
  1162. /// after filling in the vertices.
  1163. /// </summary>
  1164. /// <param name="setName">
  1165. /// Set name for this geometry for identification. Should start with &gt;
  1166. /// for dynamically created geometry to not confuse it with loaded gometry
  1167. /// from content, which uses content names.
  1168. /// </param>
  1169. /// <param name="setMaxNumberOfVertices">
  1170. /// Set maximum number of vertices to be used, can be increased if the
  1171. /// geometry is dynamic (not static).
  1172. /// </param>
  1173. /// <param name="setVertexFormat">
  1174. /// Set vertex format (usually from a shader).
  1175. /// </param>
  1176. /// <param name="setNumberOfIndices">Set number of indices</param>
  1177. /// <param name="setIsStaticData">
  1178. /// Is static data? Faster to render because does not need to be modified.
  1179. /// </param>
  1180. /// <param name="setIsLineData">
  1181. /// Set if line data is used in this vertex stream. Only used for dynamic
  1182. /// mesh data for the DrawManager. Only used without indices.
  1183. /// </param>
  1184. /// <param name="setBoundingBox">
  1185. /// Set bounding box with given data, useful for physics and displaying
  1186. /// the boundings in debug mode. For content this is usually calculated
  1187. /// already in the content system, for generated geometry it will not be
  1188. /// set usually and will be calculated automatically when first needed.
  1189. /// </param>
  1190. public GeometryData(string setName, int setMaxNumberOfVertices,
  1191. VertexFormat setVertexFormat, int setNumberOfIndices,
  1192. bool setIsStaticData, bool setIsLineData, BoundingBox setBoundingBox)
  1193. {
  1194. Name = setName;
  1195. IsStatic = setIsStaticData;
  1196. IsLineData = setIsLineData;
  1197. // Both the Skydome model and the generated sky box contain "Sky"
  1198. IsSky = Name.Contains("Sky");
  1199. Format = setVertexFormat;
  1200. VertexDataLengthInBytes = Format.LengthInBytes;
  1201. MaxNumberOfVertices = setMaxNumberOfVertices;
  1202. // Dynamic vertex data is empty by default
  1203. numberOfUsedVerticesInternal =
  1204. setIsStaticData
  1205. ? MaxNumberOfVertices
  1206. : 0;
  1207. stream = new MemoryStream(MaxNumberOfVertices * VertexDataLengthInBytes);
  1208. stream.SetLength(stream.Capacity);
  1209. writer = new BinaryWriter(stream);
  1210. reader = new BinaryReader(stream);
  1211. Indices = new ushort[setNumberOfIndices];
  1212. NumberOfUsedIndices =
  1213. setIsStaticData
  1214. ? setNumberOfIndices
  1215. : 0;
  1216. boundingBox = setBoundingBox;
  1217. }
  1218. /// <summary>
  1219. /// Create geometry data from a binary stream (e.g. as part of ModelData).
  1220. /// This constructor requires the binary stream data and a way to reload
  1221. /// the content when Dispose was called. This is used for static geometry
  1222. /// to save memory and this constructor is always used for static geometry.
  1223. /// </summary>
  1224. /// <param name="dataReader">
  1225. /// The data reader which contains the stored content data internally.
  1226. /// </param>
  1227. /// <param name="setReloadContent">
  1228. /// The delegate which contains the code for reloading the content.
  1229. /// </param>
  1230. public GeometryData(BinaryReader dataReader, RunDelegate setReloadContent)
  1231. {
  1232. // Loaded data is always static
  1233. IsStatic = true;
  1234. IsLineData = false;
  1235. reloadContent = setReloadContent;
  1236. // All data is filled in Load
  1237. Load(dataReader);
  1238. // Both the Skydome model and the generated sky box contain "Sky"
  1239. IsSky = Name.Contains("Sky");
  1240. }
  1241. #endregion
  1242. #region IDisposable Members
  1243. /// <summary>
  1244. /// Dispose all internal data (vertices and indices). The lists are still
  1245. /// valid, just empty. This is mainly used for static geometry data, which
  1246. /// can be disposed after the native objects have been created.
  1247. /// </summary>
  1248. public void Dispose()
  1249. {
  1250. if (reader != null)
  1251. {
  1252. reader.Dispose();
  1253. reader = null;
  1254. }
  1255. if (writer != null)
  1256. {
  1257. writer.Dispose();
  1258. writer = null;
  1259. }
  1260. if (stream != null)
  1261. {
  1262. stream.Dispose();
  1263. stream = null;
  1264. }
  1265. Indices = new ushort[0];
  1266. numberOfUsedVerticesInternal = 0;
  1267. // Note: Do not touch NumberOfUsedIndices or MaxNumberOfVertices as
  1268. // these are still used even for disposed geometry data objects to
  1269. // perform different re…

Large files files are truncated, but you can click here to view the full file