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
}
}