using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Krypton.Common; namespace Krypton { /// /// A hull used for casting shadows from a light source /// public class ShadowHull { #region Orientation /// /// The position of the shadow hull /// public Vector2 Position; /// /// The angle of the shadow hull /// public float Angle; #endregion #region Shape /// /// The maximum radius in which all of the shadow hull's vertices are contained, originating from the hull's position /// public float MaxRadius; /// /// The vertices comprising the shadow hull /// public ShadowHullPoint[] Points; /// /// The number of vertices comprising the shadow hull /// public int NumPoints; /// /// The indicies used to render the shadow hull /// public Int32[] Indicies; /// /// The number of indicies used to render the shadow hull /// public int NumIndicies; /// /// A value indicating if the hull should cast a shadow /// public bool Visible = true; /// /// A value indicating how much to stretch the hull /// public Vector2 Scale = Vector2.One; #endregion private ShadowHull(){} #region Factory Methods /// /// Creates a rectangular shadow hull /// /// The dimensions of the rectangle /// A rectangular shadow hull public static ShadowHull CreateRectangle(Vector2 size) { ShadowHull hull = new ShadowHull(); size *= 0.5f; hull.MaxRadius = (float)Math.Sqrt(size.X * size.X + size.Y * size.Y); hull.NumPoints = 4 * 2; var numTris = hull.NumPoints - 2; hull.NumIndicies = numTris * 3; hull.Points = new ShadowHullPoint[hull.NumPoints]; hull.Indicies = new Int32[hull.NumIndicies]; // Vertex position var posTR = new Vector2(+size.X, +size.Y); var posBR = new Vector2(+size.X, -size.Y); var posBL = new Vector2(-size.X, -size.Y); var posTL = new Vector2(-size.X, +size.Y); // Right hull.Points[0] = new ShadowHullPoint(posTR, Vector2.UnitX); hull.Points[1] = new ShadowHullPoint(posBR, Vector2.UnitX); // Bottom hull.Points[2] = new ShadowHullPoint(posBR, -Vector2.UnitY); hull.Points[3] = new ShadowHullPoint(posBL, -Vector2.UnitY); // Left hull.Points[4] = new ShadowHullPoint(posBL, -Vector2.UnitX); hull.Points[5] = new ShadowHullPoint(posTL, -Vector2.UnitX); // Top hull.Points[6] = new ShadowHullPoint(posTL, Vector2.UnitY); hull.Points[7] = new ShadowHullPoint(posTR, Vector2.UnitY); // Create tris for (int i = 0; i < numTris; i++) { hull.Indicies[i * 3 + 0] = 0; hull.Indicies[i * 3 + 1] = i + 1; hull.Indicies[i * 3 + 2] = i + 2; } return hull; } /// /// Creates a circular shadow hull /// /// radius of the circle /// number of sides the circle will be comprised of /// A circular shadow hull public static ShadowHull CreateCircle(float radius, int sides) { // Validate input if (sides < 3) { throw new ArgumentException("Shadow hull must have at least 3 sides."); } ShadowHull hull = new ShadowHull(); hull.MaxRadius = radius; // Calculate number of sides hull.NumPoints = sides * 2; var numTris = hull.NumPoints - 2; hull.NumIndicies = numTris * 3; hull.Points = new ShadowHullPoint[hull.NumPoints]; hull.Indicies = new Int32[hull.NumIndicies]; var angle = (float)(-Math.PI * 2) / sides; // XNA Renders Clockwise var angleOffset = angle / 2; for (int i = 0; i < sides; i++) { // Create vertices var v1 = new ShadowHullPoint(); var v2 = new ShadowHullPoint(); // Vertex Position v1.Position.X = (float)Math.Cos(angle * i) * radius; v1.Position.Y = (float)Math.Sin(angle * i) * radius; v2.Position.X = (float)Math.Cos(angle * (i + 1)) * radius; v2.Position.Y = (float)Math.Sin(angle * (i + 1)) * radius; // Vertex Normal v1.Normal.X = (float)Math.Cos(angle * i + angleOffset); v1.Normal.Y = (float)Math.Sin(angle * i + angleOffset); v2.Normal.X = (float)Math.Cos(angle * i + angleOffset); v2.Normal.Y = (float)Math.Sin(angle * i + angleOffset); // Copy vertices hull.Points[i * 2 + 0] = v1; hull.Points[i * 2 + 1] = v2; } for (int i = 0; i < numTris; i++) { hull.Indicies[i * 3 + 0] = 0; hull.Indicies[i * 3 + 1] = (Int32)(i + 1); hull.Indicies[i * 3 + 2] = (Int32)(i + 2); } return hull; } /// /// Creates a custom shadow hull based on a series of vertices /// /// The points which the shadow hull will be comprised of /// A custom shadow hulll public static ShadowHull CreateConvex(ref Vector2[] points) { // Validate input if (points == null) { throw new ArgumentNullException("Points cannot be null."); } if (points.Length < 3) { throw new ArgumentException("Need at least 3 points to create shadow hull."); } var numPoints = points.Length; ShadowHull hull = new ShadowHull(); hull.NumPoints = numPoints * 2; var numTris = hull.NumPoints - 2; hull.NumIndicies = numTris * 3; hull.Points = new ShadowHullPoint[hull.NumPoints]; hull.Indicies = new Int32[hull.NumIndicies]; Vector2 pointMin = points[0]; Vector2 pointMax = points[0]; for (int i = 0; i < numPoints; i++) { var p1 = points[(i + 0) % numPoints]; var p2 = points[(i + 1) % numPoints]; hull.MaxRadius = Math.Max(hull.MaxRadius, p1.Length()); var line = p2 - p1; var normal = new Vector2(-line.Y, +line.X); normal.Normalize(); hull.Points[i * 2 + 0] = new ShadowHullPoint(p1, normal); hull.Points[i * 2 + 1] = new ShadowHullPoint(p2, normal); } for (Int32 i = 0; i < numTris; i++) { hull.Indicies[i * 3 + 0] = 0; hull.Indicies[i * 3 + 1] = (Int32)(i + 1); hull.Indicies[i * 3 + 2] = (Int32)(i + 2); } return hull; } #endregion } }