PrimitiveDrawing.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using MonoGame.Extended.Triangulation;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Text;
  7. namespace MonoGame.Extended.VectorDraw
  8. {
  9. public class PrimitiveDrawing
  10. {
  11. //Drawing
  12. private PrimitiveBatch _primitiveBatch;
  13. private SpriteBatch _batch;
  14. private SpriteFont _font;
  15. private GraphicsDevice _device;
  16. private readonly Vector2[] _tempVertices = new Vector2[1000]; //TODO: something else...
  17. //private Matrix _localProjection;
  18. //private Matrix _localView;
  19. //TODO: do we need to split this based on platform?
  20. #if XBOX || WINDOWS_PHONE
  21. public const int CircleSegments = 16;
  22. #else
  23. public const int CircleSegments = 32;
  24. #endif
  25. public PrimitiveDrawing(PrimitiveBatch primitiveBatch)
  26. {
  27. _primitiveBatch = primitiveBatch;
  28. }
  29. public void DrawPoint(Vector2 center, Color color)
  30. {
  31. if (!_primitiveBatch.IsReady())
  32. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  33. //Add two points or the PrimitiveBatch acts up
  34. _primitiveBatch.AddVertex(center, color, PrimitiveType.LineList);
  35. _primitiveBatch.AddVertex(center, color, PrimitiveType.LineList);
  36. }
  37. public void DrawRectangle(Vector2 location, float width, float height, Color color)
  38. {
  39. if (!_primitiveBatch.IsReady())
  40. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  41. Vector2[] rectVerts = new Vector2[4]
  42. {
  43. new Vector2(0, 0),
  44. new Vector2(width, 0),
  45. new Vector2(width, height),
  46. new Vector2(0, height)
  47. };
  48. //Location is offset here
  49. DrawPolygon(location, rectVerts, color);
  50. }
  51. public void DrawSolidRectangle(Vector2 location, float width, float height, Color color)
  52. {
  53. if (!_primitiveBatch.IsReady())
  54. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  55. Vector2[] rectVerts = new Vector2[4]
  56. {
  57. new Vector2(0, 0),
  58. new Vector2(width, 0),
  59. new Vector2(width, height),
  60. new Vector2(0, height)
  61. };
  62. DrawSolidPolygon(location, rectVerts, color);
  63. }
  64. public void DrawCircle(Vector2 center, float radius, Color color)
  65. {
  66. if (!_primitiveBatch.IsReady())
  67. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  68. const double increment = Math.PI * 2.0 / CircleSegments;
  69. double theta = 0.0;
  70. for (int i = 0; i < CircleSegments; i++)
  71. {
  72. Vector2 v1 = center + radius * new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta));
  73. Vector2 v2 = center + radius * new Vector2((float)Math.Cos(theta + increment), (float)Math.Sin(theta + increment));
  74. _primitiveBatch.AddVertex(v1, color, PrimitiveType.LineList);
  75. _primitiveBatch.AddVertex(v2, color, PrimitiveType.LineList);
  76. theta += increment;
  77. }
  78. }
  79. public void DrawSolidCircle(Vector2 center, float radius, Color color)
  80. {
  81. if (!_primitiveBatch.IsReady())
  82. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  83. const double increment = Math.PI * 2.0 / CircleSegments;
  84. double theta = 0.0;
  85. Color colorFill = color * 0.5f;
  86. Vector2 v0 = center + radius * new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta));
  87. theta += increment;
  88. for (int i = 1; i < CircleSegments - 1; i++)
  89. {
  90. Vector2 v1 = center + radius * new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta));
  91. Vector2 v2 = center + radius * new Vector2((float)Math.Cos(theta + increment), (float)Math.Sin(theta + increment));
  92. _primitiveBatch.AddVertex(v0, colorFill, PrimitiveType.TriangleList);
  93. _primitiveBatch.AddVertex(v1, colorFill, PrimitiveType.TriangleList);
  94. _primitiveBatch.AddVertex(v2, colorFill, PrimitiveType.TriangleList);
  95. theta += increment;
  96. }
  97. DrawCircle(center, radius, color);
  98. }
  99. public void DrawSolidCircle(Vector2 center, float radius, Color color, Color fillcolor)
  100. {
  101. if (!_primitiveBatch.IsReady())
  102. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  103. const double increment = Math.PI * 2.0 / CircleSegments;
  104. double theta = 0.0;
  105. Vector2 v0 = center + radius * new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta));
  106. theta += increment;
  107. for (int i = 1; i < CircleSegments - 1; i++)
  108. {
  109. Vector2 v1 = center + radius * new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta));
  110. Vector2 v2 = center + radius * new Vector2((float)Math.Cos(theta + increment), (float)Math.Sin(theta + increment));
  111. _primitiveBatch.AddVertex(v0, fillcolor, PrimitiveType.TriangleList);
  112. _primitiveBatch.AddVertex(v1, fillcolor, PrimitiveType.TriangleList);
  113. _primitiveBatch.AddVertex(v2, fillcolor, PrimitiveType.TriangleList);
  114. theta += increment;
  115. }
  116. DrawCircle(center, radius, color);
  117. }
  118. public void DrawSegment(Vector2 start, Vector2 end, Color color)
  119. {
  120. if (!_primitiveBatch.IsReady())
  121. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  122. _primitiveBatch.AddVertex(start, color, PrimitiveType.LineList);
  123. _primitiveBatch.AddVertex(end, color, PrimitiveType.LineList);
  124. }
  125. public void DrawPolygon(Vector2 position, Vector2[] vertices, Color color, bool closed = true)
  126. {
  127. if (!_primitiveBatch.IsReady())
  128. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  129. int count = vertices.Length;
  130. for (int i = 0; i < count - 1; i++)
  131. {
  132. //translate the vertices according to the position passed
  133. _primitiveBatch.AddVertex(new Vector2(vertices[i].X + position.X, vertices[i].Y + position.Y), color, PrimitiveType.LineList);
  134. _primitiveBatch.AddVertex(new Vector2(vertices[i + 1].X + position.X, vertices[i + 1].Y + position.Y), color, PrimitiveType.LineList);
  135. }
  136. if (closed)
  137. {
  138. //TODO: verify closed is working as expected
  139. _primitiveBatch.AddVertex(new Vector2(vertices[count - 1].X + position.X, vertices[count - 1].Y + position.Y), color, PrimitiveType.LineList);
  140. _primitiveBatch.AddVertex(new Vector2(vertices[0].X + position.X, vertices[0].Y + position.Y), color, PrimitiveType.LineList);
  141. }
  142. }
  143. public void DrawSolidPolygon(Vector2 position, Vector2[] vertices, Color color, bool outline = true)
  144. {
  145. if (!_primitiveBatch.IsReady())
  146. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  147. int count = vertices.Length;
  148. if (count == 2)
  149. {
  150. DrawPolygon(position, vertices, color);
  151. return;
  152. }
  153. Color colorFill = color * (outline ? 0.5f : 1.0f);
  154. Vector2[] outVertices;
  155. int[] outIndices;
  156. Triangulator.Triangulate(vertices, WindingOrder.CounterClockwise, out outVertices, out outIndices);
  157. for(int i = 0; i < outIndices.Length - 2; i += 3)
  158. {
  159. _primitiveBatch.AddVertex(new Vector2(outVertices[outIndices[i]].X + position.X, outVertices[outIndices[i]].Y + position.Y), colorFill, PrimitiveType.TriangleList);
  160. _primitiveBatch.AddVertex(new Vector2(outVertices[outIndices[i + 1]].X + position.X, outVertices[outIndices[i + 1]].Y + position.Y), colorFill, PrimitiveType.TriangleList);
  161. _primitiveBatch.AddVertex(new Vector2(outVertices[outIndices[i + 2]].X + position.X, outVertices[outIndices[i + 2]].Y + position.Y), colorFill, PrimitiveType.TriangleList);
  162. }
  163. if (outline)
  164. DrawPolygon(position, vertices, color);
  165. }
  166. public void DrawEllipse(Vector2 center, Vector2 radius, int sides, Color color)
  167. {
  168. if (!_primitiveBatch.IsReady())
  169. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  170. DrawPolygon(center, CreateEllipse(radius.X, radius.Y, sides), color);
  171. }
  172. public void DrawSolidEllipse(Vector2 center, Vector2 radius, int sides, Color color, bool outline = true)
  173. {
  174. if (!_primitiveBatch.IsReady())
  175. throw new InvalidOperationException("BeginCustomDraw must be called before drawing anything.");
  176. Color colorFill = color * (outline ? 0.5f : 1.0f);
  177. Vector2[] vertices = CreateEllipse(radius.X, radius.Y, sides);
  178. Vector2[] outVertices;
  179. int[] outIndices;
  180. Triangulator.Triangulate(vertices, WindingOrder.CounterClockwise, out outVertices, out outIndices);
  181. for (int i = 0; i < outIndices.Length - 2; i += 3)
  182. {
  183. _primitiveBatch.AddVertex(new Vector2(outVertices[outIndices[i]].X + center.X, outVertices[outIndices[i]].Y + center.Y), colorFill, PrimitiveType.TriangleList);
  184. _primitiveBatch.AddVertex(new Vector2(outVertices[outIndices[i + 1]].X + center.X, outVertices[outIndices[i + 1]].Y + center.Y), colorFill, PrimitiveType.TriangleList);
  185. _primitiveBatch.AddVertex(new Vector2(outVertices[outIndices[i + 2]].X + center.X, outVertices[outIndices[i + 2]].Y + center.Y), colorFill, PrimitiveType.TriangleList);
  186. }
  187. }
  188. private static Vector2[] CreateEllipse(float rx, float ry, int sides)
  189. {
  190. var vertices = new Vector2[sides];
  191. var t = 0.0;
  192. var dt = 2.0 * Math.PI / sides;
  193. for (var i = 0; i < sides; i++, t += dt)
  194. {
  195. var x = (float)(rx * Math.Cos(t));
  196. var y = (float)(ry * Math.Sin(t));
  197. vertices[i] = new Vector2(x, y);
  198. }
  199. return vertices;
  200. }
  201. }
  202. }