using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Krypton.Common; namespace Krypton.Lights { public class Light2D : ILight2D { private bool mIsOn = true; private Vector2 mPosition = Vector2.Zero; private float mAngle = 0; private Texture2D mTexture = null; private Color mColor = Color.White; private float mRange = 1; private float mFov = MathHelper.TwoPi; private float mIntensity = 1; #region Parameters /// /// The light's position /// public Vector2 Position { get { return this.mPosition; } set { this.mPosition = value; } } /// /// The X coordinate of the light's position /// public float X { get { return this.mPosition.X; } set { this.mPosition.X = value; } } /// /// The Y coordinate of the light's position /// public float Y { get { return this.mPosition.Y; } set { this.mPosition.Y = value; } } /// /// The light's angle /// public float Angle { get { return this.mAngle; } set { this.mAngle = value; } } /// /// The texture used as the base light map, from which shadows will be subtracted /// public Texture2D Texture { get { return this.mTexture; } set { this.mTexture = value; } } /// /// The color used to tint the light's texture /// public Color Color { get { return this.mColor; } set { this.mColor = value; } } /// /// The light's maximum radius (or "half width", if you will) /// public float Range { get { return this.mRange; } set { this.mRange = value; } } /// /// Gets or sets the light's field of view. This value determines the angles at which the light will cease to draw /// public float Fov { get { return this.mFov; } set { this.mFov = MathHelper.Clamp(value, 0, MathHelper.TwoPi); } } /// /// Gets or sets the light's intensity. Think pixel = (tex * color) ^ (1 / intensity) /// public float Intensity { get { return this.mIntensity; } set { this.mIntensity = MathHelper.Clamp(value, 0.01f, 3f); } } #endregion Parameters /// /// Gets or sets a value indicating weither or not to draw the light /// public bool IsOn { get { return this.mIsOn; } set { this.mIsOn = value; } } /// /// Draws shadows from the light's position outward /// /// A render helper for drawing shadows /// The shadow hulls used to draw shadows public void Draw(KryptonRenderHelper helper, List hulls) { // Draw the light only if it's on if (!this.mIsOn) return; // Make sure we only render the following hulls helper.ShadowHullVertices.Clear(); helper.ShadowHullIndicies.Clear(); // Loop through each hull foreach (ShadowHull hull in hulls) { //if(hull.Bounds.Intersects(this.Bounds)) // Add the hulls to the buffer only if they are within the light's range if (hull.Visible && Light2D.IsInRange(hull.Position - this.Position, hull.MaxRadius * Math.Max(hull.Scale.X, hull.Scale.Y) + this.Range)) { helper.BufferAddShadowHull(hull); } } var shadowEffect = helper.Effect.Techniques["PointLight_Shadow_Fast"]; helper.Effect.CurrentTechnique = shadowEffect; // Set the effect parameters helper.Effect.Parameters["LightPosition"].SetValue(this.mPosition); helper.Effect.Parameters["Texture0"].SetValue(this.mTexture); helper.Effect.Parameters["LightIntensityFactor"].SetValue(1 / (this.mIntensity * this.mIntensity)); shadowEffect.Passes["ShadowStencil"].Apply(); helper.BufferDraw(); shadowEffect.Passes["Light"].Apply(); helper.DrawClippedFov(this.mPosition, this.mAngle, this.mRange * 2, this.mColor, this.mFov); } /// /// Determines if a vector's length is less than a specified value /// /// Offset /// Distance /// private static bool IsInRange(Vector2 offset, float dist) { if (offset.X * offset.X + offset.Y * offset.Y < dist * dist) return true; return false; } /// /// Gets the world-space bounds which contain the light /// public BoundingRect Bounds { get { BoundingRect rect; rect.Min.X = this.mPosition.X - this.mRange; rect.Min.Y = this.mPosition.Y - this.mRange; rect.Max.X = this.mPosition.X + this.mRange; rect.Max.Y = this.mPosition.Y + this.mRange; return rect; } } } }