ParticleSystemGizmos.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using bs;
  4. namespace bs.Editor
  5. {
  6. /** @addtogroup Gizmos
  7. * @{
  8. */
  9. /// <summary>
  10. /// Handles drawing of gizmos and selection callback for the <see cref="ParticleSystem"/> component.
  11. /// </summary>
  12. internal class ParticleSystemGizmos
  13. {
  14. /// <summary>
  15. /// Draws two concentric arcs, depending on the <paramref name="thickness"/> parameter.
  16. /// </summary>
  17. /// <param name="center">Position of the arc center</param>
  18. /// <param name="radius">Radius of the outer arc.</param>
  19. /// <param name="arc">Length of the arc in range [0, 360] degrees.</param>
  20. /// <param name="thickness">Determines radius of the inner arc, in range [0, 1]. 0 specifies the inner arc is
  21. /// the same radius as the outer arc, while 1 specifies the inner arc has zero radius.</param>
  22. /// <param name="color">Color to draw the arc with.</param>
  23. private static void DrawArcWithThickness(Vector3 center, float radius, Radian arc, float thickness, Color color)
  24. {
  25. Gizmos.Color = color;
  26. if (arc.Degrees > 0.0f)
  27. {
  28. Gizmos.DrawWireArc(center, -Vector3.ZAxis, radius, new Degree(0.0f), arc);
  29. if (thickness > 0.0f)
  30. {
  31. float innerRadius = radius * (1.0f - MathEx.Clamp01(thickness));
  32. if (thickness < 1.0f)
  33. {
  34. Gizmos.Color = color * new Color(0.5f, 0.5f, 0.5f);
  35. Gizmos.DrawWireArc(center, -Vector3.ZAxis, innerRadius, new Degree(0.0f), arc);
  36. }
  37. Gizmos.Color = color * new Color(0.25f, 0.25f, 0.25f);
  38. for (Radian x = new Radian(0.0f); x < arc; x += MathEx.HalfPi)
  39. {
  40. Vector3 dir = new Vector3(MathEx.Cos(x), MathEx.Sin(x), 0.0f);
  41. Gizmos.DrawLine(center + dir * innerRadius, center + dir * radius);
  42. }
  43. if (!MathEx.ApproxEquals(arc.Degrees % 90.0f, 0.0f))
  44. {
  45. Vector3 dir = new Vector3(MathEx.Cos(arc), MathEx.Sin(arc), 0.0f);
  46. Gizmos.DrawLine(center + dir * innerRadius, center + dir * radius);
  47. }
  48. }
  49. }
  50. }
  51. /// <summary>
  52. /// Draws a border between two arcs.
  53. /// </summary>
  54. /// <param name="topRadius">Radius of the top arc.</param>
  55. /// <param name="baseRadius">Radius of the base arc.</param>
  56. /// <param name="arc">Length of the arc in range [0, 360] degrees.</param>
  57. /// <param name="length">Distance between the two arcs.</param>
  58. private static void DrawConeBorder(float topRadius, float baseRadius, Radian arc, float length)
  59. {
  60. for (Radian x = new Radian(0.0f); x < arc; x += MathEx.HalfPi)
  61. {
  62. Vector3 dir = new Vector3(MathEx.Cos(x), MathEx.Sin(x), 0.0f);
  63. Vector3 a = dir * baseRadius + new Vector3(0.0f, 0.0f, -length);
  64. Vector3 b = dir * topRadius;
  65. Gizmos.DrawLine(a, b);
  66. }
  67. if (!MathEx.ApproxEquals(arc.Degrees % 90.0f, 0.0f))
  68. {
  69. Vector3 dir = new Vector3(MathEx.Cos(arc), MathEx.Sin(arc), 0.0f);
  70. Vector3 a = dir * baseRadius + new Vector3(0.0f, 0.0f, -length);
  71. Vector3 b = dir * topRadius;
  72. Gizmos.DrawLine(a, b);
  73. }
  74. }
  75. /// <summary>
  76. /// Draws the particle system shape when the particle system is selected.
  77. /// </summary>
  78. /// <param name="component">Component to draw gizmos for.</param>
  79. [DrawGizmo(DrawGizmoFlags.Selected)]
  80. private static void Draw(ParticleSystem component)
  81. {
  82. SceneObject so = component.SceneObject;
  83. // Draw collision planes, if enabled
  84. Gizmos.Color = Color.Yellow;
  85. ParticleEvolver[] evolvers = component.Evolvers;
  86. foreach (var entry in evolvers)
  87. {
  88. if (entry is ParticleCollisions collisions)
  89. {
  90. if (collisions.Options.mode == ParticleCollisionMode.Plane)
  91. {
  92. void DrawPlane(Vector3 position, Vector3 right, Vector3 up, Vector3 forward)
  93. {
  94. Vector3 topLeft = position - right + up;
  95. Vector3 topRight = position + right + up;
  96. Vector3 botLeft = position - right - up;
  97. Vector3 botRight = position + right - up;
  98. Gizmos.DrawLine(topLeft, topRight);
  99. Gizmos.DrawLine(topRight, botRight);
  100. Gizmos.DrawLine(botRight, botLeft);
  101. Gizmos.DrawLine(botLeft, topLeft);
  102. Gizmos.DrawLine(position, position + forward * 0.4f);
  103. }
  104. SceneObject[] planeObjects = collisions.PlaneObjects;
  105. foreach (var planeSO in planeObjects)
  106. {
  107. Vector3 position = planeSO.Position;
  108. Vector3 right = planeSO.Rotation.Rotate(Vector3.XAxis);
  109. Vector3 up = planeSO.Rotation.Rotate(Vector3.YAxis);
  110. Vector3 forward = planeSO.Forward;
  111. DrawPlane(position, right, up, forward);
  112. }
  113. Plane[] planes = collisions.Planes;
  114. foreach (var plane in planes)
  115. {
  116. Vector3 right, up;
  117. Vector3.OrthogonalComplement(plane.normal, out right, out up);
  118. DrawPlane(plane.normal * plane.d, right, up, plane.normal);
  119. }
  120. }
  121. }
  122. }
  123. // Draw emitter shapes
  124. Gizmos.Transform = Matrix4.TRS(so.Position, Quaternion.LookRotation(so.Rotation.Forward, so.Rotation.Up), Vector3.One);
  125. Gizmos.Color = Color.Green;
  126. ParticleEmitter[] emitters = component.Emitters;
  127. foreach (var entry in emitters)
  128. {
  129. ParticleEmitterShape shape = entry?.Shape;
  130. if (shape == null)
  131. continue;
  132. if (shape is ParticleEmitterBoxShape boxShape)
  133. Gizmos.DrawWireCube(Vector3.Zero, boxShape.Options.extents);
  134. else if (shape is ParticleEmitterSphereShape sphereShape)
  135. {
  136. ParticleSphereShapeOptions options = sphereShape.Options;
  137. Gizmos.DrawWireSphere(Vector3.Zero, options.radius);
  138. if (options.thickness > 0.0f)
  139. {
  140. float innerRadius = options.radius * (1.0f - MathEx.Clamp01(options.thickness));
  141. if (options.thickness < 1.0f)
  142. {
  143. Gizmos.Color = Color.Green * new Color(0.5f, 0.5f, 0.5f);
  144. Gizmos.DrawWireSphere(Vector3.Zero, innerRadius);
  145. }
  146. Gizmos.Color = Color.Green * new Color(0.25f, 0.25f, 0.25f);
  147. Gizmos.DrawLine(Vector3.XAxis * innerRadius, Vector3.XAxis * options.radius);
  148. Gizmos.DrawLine(-Vector3.XAxis * innerRadius, -Vector3.XAxis * options.radius);
  149. Gizmos.DrawLine(Vector3.YAxis * innerRadius, Vector3.YAxis * options.radius);
  150. Gizmos.DrawLine(-Vector3.YAxis * innerRadius, -Vector3.YAxis * options.radius);
  151. Gizmos.DrawLine(Vector3.ZAxis * innerRadius, Vector3.ZAxis * options.radius);
  152. Gizmos.DrawLine(-Vector3.ZAxis * innerRadius, -Vector3.ZAxis * options.radius);
  153. }
  154. }
  155. else if (shape is ParticleEmitterHemisphereShape hemisphereShape)
  156. {
  157. ParticleHemisphereShapeOptions options = hemisphereShape.Options;
  158. Gizmos.DrawWireHemisphere(Vector3.Zero, options.radius);
  159. DrawArcWithThickness(Vector3.Zero, options.radius, new Degree(360.0f), options.thickness, Color.Green);
  160. if (options.thickness > 0.0f)
  161. {
  162. float innerRadius = options.radius * (1.0f - MathEx.Clamp01(options.thickness));
  163. if (options.thickness < 1.0f)
  164. {
  165. Gizmos.Color = Color.Green * new Color(0.5f, 0.5f, 0.5f);
  166. Gizmos.DrawWireHemisphere(Vector3.Zero, innerRadius);
  167. }
  168. Gizmos.Color = Color.Green * new Color(0.25f, 0.25f, 0.25f);
  169. Gizmos.DrawLine(Vector3.XAxis * innerRadius, Vector3.XAxis * options.radius);
  170. Gizmos.DrawLine(-Vector3.XAxis * innerRadius, -Vector3.XAxis * options.radius);
  171. Gizmos.DrawLine(Vector3.YAxis * innerRadius, Vector3.YAxis * options.radius);
  172. Gizmos.DrawLine(-Vector3.YAxis * innerRadius, -Vector3.YAxis * options.radius);
  173. Gizmos.DrawLine(Vector3.ZAxis * innerRadius, Vector3.ZAxis * options.radius);
  174. }
  175. }
  176. else if (shape is ParticleEmitterCircleShape circleShape)
  177. {
  178. ParticleCircleShapeOptions options = circleShape.Options;
  179. DrawArcWithThickness(Vector3.Zero, options.radius, options.arc, options.thickness, Color.Green);
  180. }
  181. else if (shape is ParticleEmitterLineShape lineShape)
  182. {
  183. float halfLength = lineShape.Options.length * 0.5f;
  184. Gizmos.DrawLine(new Vector3(-halfLength, 0.0f, 0.0f), new Vector3(halfLength, 0.0f, 0.0f));
  185. }
  186. else if (shape is ParticleEmitterRectShape rectShape)
  187. {
  188. Vector2 extents = rectShape.Options.extents;
  189. Vector3 right = new Vector3(extents.x, 0.0f, 0.0f);
  190. Vector3 up = new Vector3(0.0f, extents.y, 0.0f);
  191. Vector3 topLeft = -right + up;
  192. Vector3 topRight = right + up;
  193. Vector3 botLeft = -right - up;
  194. Vector3 botRight = right - up;
  195. Gizmos.DrawLine(topLeft, topRight);
  196. Gizmos.DrawLine(topRight, botRight);
  197. Gizmos.DrawLine(botRight, botLeft);
  198. Gizmos.DrawLine(botLeft, topLeft);
  199. }
  200. else if (shape is ParticleEmitterConeShape coneShape)
  201. {
  202. ParticleConeShapeOptions options = coneShape.Options;
  203. float topRadius = options.radius;
  204. float baseRadius = options.length * MathEx.Tan(options.angle);
  205. Radian arc = options.arc;
  206. if (topRadius > 0.0f)
  207. DrawArcWithThickness(Vector3.Zero, topRadius, arc, options.thickness, Color.Green);
  208. DrawArcWithThickness(new Vector3(0.0f, 0.0f, -options.length), baseRadius, arc, options.thickness,
  209. Color.Green);
  210. Gizmos.Color = Color.Green;
  211. DrawConeBorder(topRadius, baseRadius, arc, options.length);
  212. if (options.thickness > 0.0f && options.thickness < 1.0f)
  213. {
  214. Gizmos.Color = Color.Green * new Color(0.5f, 0.5f, 0.5f);
  215. DrawConeBorder(
  216. topRadius * (1.0f - MathEx.Clamp01(options.thickness)),
  217. baseRadius * (1.0f - MathEx.Clamp01(options.thickness)), arc, options.length);
  218. }
  219. }
  220. else if (shape is ParticleEmitterStaticMeshShape staticMeshShape)
  221. {
  222. RRef<Mesh> mesh = staticMeshShape.Options.mesh;
  223. if(mesh.IsLoaded)
  224. Gizmos.DrawWireMesh(mesh.Value.MeshData);
  225. }
  226. else if (shape is ParticleEmitterSkinnedMeshShape skinnedMeshShape)
  227. {
  228. Renderable renderable = skinnedMeshShape.Options.renderable;
  229. if (renderable != null)
  230. {
  231. RRef<Mesh> mesh = renderable.Mesh;
  232. if (mesh.IsLoaded)
  233. Gizmos.DrawWireMesh(mesh.Value.MeshData);
  234. }
  235. }
  236. }
  237. // Draw manual bounds
  238. if (!component.Settings.UseAutomaticBounds)
  239. {
  240. Gizmos.Color = Color.LightGray;
  241. AABox bounds = component.Settings.CustomBounds;
  242. Gizmos.DrawWireCube(bounds.Center, bounds.Size * 0.5f);
  243. }
  244. }
  245. /// <summary>
  246. /// Draws particle system icon in scene view.
  247. /// </summary>
  248. /// <param name="component">Component to draw the icon for.</param>
  249. [DrawGizmo(DrawGizmoFlags.NotSelected | DrawGizmoFlags.Pickable)]
  250. private static void DrawIcon(ParticleSystem component)
  251. {
  252. Gizmos.DrawIcon(component.SceneObject.Position, EditorBuiltin.GetSceneViewIcon(SceneViewIcon.ParticleSystem), false);
  253. }
  254. /// <summary>
  255. /// Method called by the runtime when the component is selected or deselected.
  256. /// </summary>
  257. /// <param name="component">Component that was selected or deselected.</param>
  258. /// <param name="selected">If true the provided component was selected, otherwise deselected.</param>
  259. [OnSelectionChanged]
  260. private static void SelectionChanged(ParticleSystem component, bool selected)
  261. {
  262. component.TogglePreviewMode(selected);
  263. }
  264. }
  265. /** @} */
  266. }