Executable
+278
@@ -0,0 +1,278 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Krypton.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Much like Rectangle, but stored as two Vector2s
|
||||
/// </summary>
|
||||
public struct BoundingRect
|
||||
{
|
||||
public Vector2 Min;
|
||||
public Vector2 Max;
|
||||
|
||||
public float Left { get { return this.Min.X; } }
|
||||
public float Right { get { return this.Max.X; } }
|
||||
public float Top { get { return this.Max.Y; } }
|
||||
public float Bottom { get { return this.Min.Y; } }
|
||||
|
||||
public float Width { get { return this.Max.X - this.Min.X; } }
|
||||
public float Height { get { return this.Max.Y - this.Min.Y; } }
|
||||
|
||||
private static BoundingRect mEmpty;
|
||||
private static BoundingRect mMinMax;
|
||||
|
||||
static BoundingRect()
|
||||
{
|
||||
BoundingRect.mEmpty = new BoundingRect();
|
||||
BoundingRect.mMinMax = new BoundingRect(Vector2.One * float.MinValue, Vector2.One * float.MaxValue);
|
||||
}
|
||||
|
||||
public Vector2 Center
|
||||
{
|
||||
get { return (this.Min + this.Max) / 2; }
|
||||
}
|
||||
|
||||
public static BoundingRect Empty
|
||||
{
|
||||
get { return BoundingRect.mEmpty; }
|
||||
}
|
||||
|
||||
|
||||
public static BoundingRect MinMax
|
||||
{
|
||||
get { return BoundingRect.mMinMax; }
|
||||
}
|
||||
|
||||
public bool IsZero
|
||||
{
|
||||
get
|
||||
{
|
||||
return
|
||||
(this.Min.X == 0) &&
|
||||
(this.Min.Y == 0) &&
|
||||
(this.Max.X == 0) &&
|
||||
(this.Max.Y == 0);
|
||||
}
|
||||
}
|
||||
|
||||
public BoundingRect(float x, float y, float width, float height)
|
||||
{
|
||||
this.Min.X = x;
|
||||
this.Min.Y = y;
|
||||
this.Max.X = x + width;
|
||||
this.Max.Y = y + height;
|
||||
}
|
||||
|
||||
public BoundingRect(Vector2 min, Vector2 max)
|
||||
{
|
||||
this.Min = min;
|
||||
this.Max = max;
|
||||
}
|
||||
|
||||
public bool Contains(float x, float y)
|
||||
{
|
||||
return
|
||||
(this.Min.X <= x) &&
|
||||
(this.Min.Y <= y) &&
|
||||
(this.Max.X >= x) &&
|
||||
(this.Max.Y >= y);
|
||||
}
|
||||
|
||||
public bool Contains(Vector2 vector)
|
||||
{
|
||||
return
|
||||
(this.Min.X <= vector.X) &&
|
||||
(this.Min.Y <= vector.Y) &&
|
||||
(this.Max.X >= vector.X) &&
|
||||
(this.Max.Y >= vector.Y);
|
||||
}
|
||||
|
||||
public void Contains(ref Vector2 rect, out bool result)
|
||||
{
|
||||
result =
|
||||
(this.Min.X <= rect.X) &&
|
||||
(this.Min.Y <= rect.Y) &&
|
||||
(this.Max.X >= rect.X) &&
|
||||
(this.Max.Y >= rect.Y);
|
||||
}
|
||||
|
||||
public bool Contains(BoundingRect rect)
|
||||
{
|
||||
return
|
||||
(this.Min.X <= rect.Min.X) &&
|
||||
(this.Min.Y <= rect.Min.Y) &&
|
||||
(this.Max.X >= rect.Max.X) &&
|
||||
(this.Max.Y >= rect.Max.Y);
|
||||
}
|
||||
|
||||
public void Contains(ref BoundingRect rect, out bool result)
|
||||
{
|
||||
result =
|
||||
(this.Min.X <= rect.Min.X) &&
|
||||
(this.Min.Y <= rect.Min.Y) &&
|
||||
(this.Max.X >= rect.Max.X) &&
|
||||
(this.Max.Y >= rect.Max.Y) ;
|
||||
}
|
||||
|
||||
public bool Intersects(BoundingRect rect)
|
||||
{
|
||||
return
|
||||
(this.Min.X < rect.Max.X) &&
|
||||
(this.Min.Y < rect.Max.Y) &&
|
||||
(this.Max.X > rect.Min.X) &&
|
||||
(this.Max.Y > rect.Min.Y);
|
||||
}
|
||||
|
||||
public void Intersects(ref BoundingRect rect, out bool result)
|
||||
{
|
||||
result =
|
||||
(this.Min.X < rect.Max.X) &&
|
||||
(this.Min.Y < rect.Max.Y) &&
|
||||
(this.Max.X > rect.Min.X) &&
|
||||
(this.Max.Y > rect.Min.Y);
|
||||
}
|
||||
|
||||
public static BoundingRect Intersect(BoundingRect rect1, BoundingRect rect2)
|
||||
{
|
||||
BoundingRect result;
|
||||
|
||||
float num8 = rect1.Max.X;
|
||||
float num7 = rect2.Max.X;
|
||||
float num6 = rect1.Max.Y;
|
||||
float num5 = rect2.Max.Y;
|
||||
float num2 = (rect1.Min.X > rect2.Min.X) ? rect1.Min.X : rect2.Min.X;
|
||||
float num = (rect1.Min.Y > rect2.Min.Y) ? rect1.Min.Y : rect2.Min.Y;
|
||||
float num4 = (num8 < num7) ? num8 : num7;
|
||||
float num3 = (num6 < num5) ? num6 : num5;
|
||||
|
||||
if ((num4 > num2) && (num3 > num))
|
||||
{
|
||||
result.Min.X = num2;
|
||||
result.Min.Y = num;
|
||||
result.Max.X = num4;
|
||||
result.Max.Y = num3;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Min.X = 0;
|
||||
result.Min.Y = 0;
|
||||
result.Max.X = 0;
|
||||
result.Max.Y = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Intersect(ref BoundingRect rect1, ref BoundingRect rect2, out BoundingRect result)
|
||||
{
|
||||
float num8 = rect1.Max.X;
|
||||
float num7 = rect2.Max.X;
|
||||
float num6 = rect1.Max.Y;
|
||||
float num5 = rect2.Max.Y;
|
||||
float num2 = (rect1.Min.X > rect2.Min.X) ? rect1.Min.X : rect2.Min.X;
|
||||
float num = (rect1.Min.Y > rect2.Min.Y) ? rect1.Min.Y : rect2.Min.Y;
|
||||
float num4 = (num8 < num7) ? num8 : num7;
|
||||
float num3 = (num6 < num5) ? num6 : num5;
|
||||
|
||||
if ((num4 > num2) && (num3 > num))
|
||||
{
|
||||
result.Min.X = num2;
|
||||
result.Min.Y = num;
|
||||
result.Max.X = num4;
|
||||
result.Max.Y = num3;
|
||||
}
|
||||
|
||||
result.Min.X = 0;
|
||||
result.Min.Y = 0;
|
||||
result.Max.X = 0;
|
||||
result.Max.Y = 0;
|
||||
}
|
||||
|
||||
public static BoundingRect Union(BoundingRect rect1, BoundingRect rect2)
|
||||
{
|
||||
BoundingRect result;
|
||||
|
||||
float num6 = rect1.Max.X;
|
||||
float num5 = rect2.Max.X;
|
||||
float num4 = rect1.Max.Y;
|
||||
float num3 = rect2.Max.Y;
|
||||
float num2 = (rect1.Min.X < rect2.Min.X) ? rect1.Min.X : rect2.Min.X;
|
||||
float num = (rect1.Min.Y < rect2.Min.Y) ? rect1.Min.Y : rect2.Min.Y;
|
||||
float num8 = (num6 > num5) ? num6 : num5;
|
||||
float num7 = (num4 > num3) ? num4 : num3;
|
||||
|
||||
result.Min.X = num2;
|
||||
result.Min.Y = num;
|
||||
result.Max.X = num8;
|
||||
result.Max.Y = num7;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Union(ref BoundingRect rect1, ref BoundingRect rect2, out BoundingRect result)
|
||||
{
|
||||
float num6 = rect1.Max.X;
|
||||
float num5 = rect2.Max.X;
|
||||
float num4 = rect1.Max.Y;
|
||||
float num3 = rect2.Max.Y;
|
||||
float num2 = (rect1.Min.X < rect2.Min.X) ? rect1.Min.X : rect2.Min.X;
|
||||
float num = (rect1.Min.Y < rect2.Min.Y) ? rect1.Min.Y : rect2.Min.Y;
|
||||
float num8 = (num6 > num5) ? num6 : num5;
|
||||
float num7 = (num4 > num3) ? num4 : num3;
|
||||
|
||||
result.Min.X = num2;
|
||||
result.Min.Y = num;
|
||||
result.Max.X = num8;
|
||||
result.Max.Y = num7;
|
||||
}
|
||||
|
||||
public bool Equals(BoundingRect other)
|
||||
{
|
||||
return
|
||||
(this.Min.X == other.Min.X) &&
|
||||
(this.Min.Y == other.Min.Y) &&
|
||||
(this.Max.X == other.Max.X) &&
|
||||
(this.Max.Y == other.Max.Y);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Min.GetHashCode() + this.Max.GetHashCode();
|
||||
}
|
||||
|
||||
public static bool operator ==(BoundingRect a, BoundingRect b)
|
||||
{
|
||||
return
|
||||
(a.Min.X == b.Min.X) &&
|
||||
(a.Min.Y == b.Min.Y) &&
|
||||
(a.Max.X == b.Max.X) &&
|
||||
(a.Max.Y == b.Max.Y);
|
||||
}
|
||||
|
||||
public static bool operator !=(BoundingRect a, BoundingRect b)
|
||||
{
|
||||
return
|
||||
(a.Min.X != b.Min.X) ||
|
||||
(a.Min.Y != b.Min.Y) ||
|
||||
(a.Max.X != b.Max.X) ||
|
||||
(a.Max.Y != b.Max.Y);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is BoundingRect)
|
||||
{
|
||||
return this == (BoundingRect)obj;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{B1BFCB2A-C001-4DAC-BA7D-B68BF28757F3}</ProjectGuid>
|
||||
<ProjectTypeGuids>{6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Krypton</RootNamespace>
|
||||
<AssemblyName>Krypton</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
|
||||
<XnaFrameworkVersion>v4.0</XnaFrameworkVersion>
|
||||
<XnaPlatform>Windows</XnaPlatform>
|
||||
<XnaProfile>HiDef</XnaProfile>
|
||||
<XnaCrossPlatformGroupID>f22a3b67-7b26-4bcf-9fa4-831713f618f0</XnaCrossPlatformGroupID>
|
||||
<XnaOutputType>Library</XnaOutputType>
|
||||
<SccProjectName>
|
||||
</SccProjectName>
|
||||
<SccLocalPath>
|
||||
</SccLocalPath>
|
||||
<SccAuxPath>
|
||||
</SccAuxPath>
|
||||
<SccProvider>
|
||||
</SccProvider>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\x86\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;WINDOWS</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<XnaCompressContent>false</XnaCompressContent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\x86\Release</OutputPath>
|
||||
<DefineConstants>TRACE;WINDOWS</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<XnaCompressContent>true</XnaCompressContent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="mscorlib">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Common\BoundingRect.cs" />
|
||||
<Compile Include="KryptonEngine.cs" />
|
||||
<Compile Include="Lights\ILight2D.cs" />
|
||||
<Compile Include="LightTextureBuilder.cs" />
|
||||
<Compile Include="Lights\Light2D.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="KryptonRenderHelper.cs" />
|
||||
<Compile Include="ShadowHull.cs" />
|
||||
<Compile Include="ShadowHullPoint.cs" />
|
||||
<Compile Include="ShadowHullVertex.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />
|
||||
<!--
|
||||
To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
Executable
+423
@@ -0,0 +1,423 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using Microsoft.Xna.Framework.GamerServices;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using Microsoft.Xna.Framework.Media;
|
||||
|
||||
using Krypton.Lights;
|
||||
using Krypton.Common;
|
||||
|
||||
|
||||
namespace Krypton
|
||||
{
|
||||
public enum LightMapSize
|
||||
{
|
||||
Full = 1,
|
||||
Fourth = 2,
|
||||
Eighth = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A GPU-based 2D lighting engine
|
||||
/// </summary>
|
||||
public class KryptonEngine : DrawableGameComponent
|
||||
{
|
||||
// The Krypton Effect
|
||||
private string mEffectAssetName;
|
||||
private Effect mEffect;
|
||||
private CullMode mCullMode = CullMode.CullCounterClockwiseFace;
|
||||
|
||||
// The goods
|
||||
private List<ShadowHull> mHulls = new List<ShadowHull>();
|
||||
private List<ILight2D> mLights = new List<ILight2D>();
|
||||
|
||||
// World View Projection matrix, and it's min and max view bounds
|
||||
private Matrix mWVP = Matrix.Identity;
|
||||
private bool mSpriteBatchCompatabilityEnabled = false;
|
||||
private BoundingRect mBounds = BoundingRect.MinMax;
|
||||
|
||||
// Blur
|
||||
private float mBluriness = 0.25f;
|
||||
private RenderTarget2D mMapBlur;
|
||||
|
||||
// Light maps
|
||||
private RenderTarget2D mMap;
|
||||
private Color mAmbientColor = new Color(35,35,35);
|
||||
private LightMapSize mLightMapSize = LightMapSize.Full;
|
||||
|
||||
/// <summary>
|
||||
/// Krypton's render helper. It helps render. It also needs to be re-written.
|
||||
/// </summary>
|
||||
public KryptonRenderHelper RenderHelper { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how Krypton should cull geometry. The default value is CullMode.CounterClockwise
|
||||
/// </summary>
|
||||
public CullMode CullMode
|
||||
{
|
||||
get { return this.mCullMode; }
|
||||
set { this.mCullMode = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection of lights krypton uses to render shadows
|
||||
/// </summary>
|
||||
public List<ILight2D> Lights { get { return this.mLights; } }
|
||||
|
||||
/// <summary>
|
||||
/// The collection of hulls krypton uses to render shadows
|
||||
/// </summary>
|
||||
public List<ShadowHull> Hulls { get { return this.mHulls; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the matrix used to draw the light map. This should match your scene's matrix.
|
||||
/// </summary>
|
||||
public Matrix Matrix
|
||||
{
|
||||
get { return this.mWVP; }
|
||||
set
|
||||
{
|
||||
if (this.mWVP != value)
|
||||
{
|
||||
this.mWVP = value;
|
||||
|
||||
// This is totally ghetto, but it works for now. :)
|
||||
// Compute the world-space bounds of the given matrix
|
||||
var inverse = Matrix.Invert(value);
|
||||
|
||||
var v1 = Vector2.Transform(new Vector2(1, 1), inverse);
|
||||
var v2 = Vector2.Transform(new Vector2(1, -1), inverse);
|
||||
var v3 = Vector2.Transform(new Vector2(-1, -1), inverse);
|
||||
var v4 = Vector2.Transform(new Vector2(-1, 1), inverse);
|
||||
|
||||
this.mBounds.Min = v1;
|
||||
this.mBounds.Min = Vector2.Min(this.mBounds.Min, v2);
|
||||
this.mBounds.Min = Vector2.Min(this.mBounds.Min, v3);
|
||||
this.mBounds.Min = Vector2.Min(this.mBounds.Min, v4);
|
||||
|
||||
this.mBounds.Max = v1;
|
||||
this.mBounds.Max = Vector2.Max(this.mBounds.Max, v2);
|
||||
this.mBounds.Max = Vector2.Max(this.mBounds.Max, v3);
|
||||
this.mBounds.Max = Vector2.Max(this.mBounds.Max, v4);
|
||||
|
||||
this.mBounds = BoundingRect.MinMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating weither or not to use SpriteBatch's matrix when drawing lightmaps
|
||||
/// </summary>
|
||||
public bool SpriteBatchCompatablityEnabled
|
||||
{
|
||||
get { return this.mSpriteBatchCompatabilityEnabled; }
|
||||
set { this.mSpriteBatchCompatabilityEnabled = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ambient color of the light map. Lights + AmbientColor = Final
|
||||
/// </summary>
|
||||
public Color AmbientColor
|
||||
{
|
||||
get { return this.mAmbientColor; }
|
||||
set { this.mAmbientColor = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value used to determine light map size
|
||||
/// </summary>
|
||||
public LightMapSize LightMapSize
|
||||
{
|
||||
get { return this.mLightMapSize; }
|
||||
set
|
||||
{
|
||||
if (this.mLightMapSize != value)
|
||||
{
|
||||
this.mLightMapSize = value;
|
||||
this.DisposeRenderTargets();
|
||||
this.CreateRenderTargets();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how much to blur the final light map. If the value is zero, the lightmap will not be blurred
|
||||
/// </summary>
|
||||
public float Bluriness
|
||||
{
|
||||
get { return this.mBluriness; }
|
||||
set { this.mBluriness = Math.Max(0, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of krypton
|
||||
/// </summary>
|
||||
/// <param name="game">Your game object</param>
|
||||
/// <param name="effectAssetName">The asset name of Krypton's effect file, which must be included in your content project</param>
|
||||
public KryptonEngine(Game game, string effectAssetName)
|
||||
: base(game)
|
||||
{
|
||||
this.mEffectAssetName = effectAssetName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes Krpyton, and hooks itself to the graphics device
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
this.GraphicsDevice.DeviceReset += new EventHandler<EventArgs>(GraphicsDevice_DeviceReset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets kryptons graphics device resources
|
||||
/// </summary>
|
||||
private void GraphicsDevice_DeviceReset(object sender, EventArgs e)
|
||||
{
|
||||
this.DisposeRenderTargets();
|
||||
this.CreateRenderTargets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load's the graphics related content required to draw light maps
|
||||
/// </summary>
|
||||
protected override void LoadContent()
|
||||
{
|
||||
// This needs to better handle content loading...
|
||||
// if the window is resized, Krypton needs to notice.
|
||||
this.mEffect = this.Game.Content.Load<Effect>(this.mEffectAssetName);
|
||||
this.RenderHelper = new KryptonRenderHelper(this.GraphicsDevice, this.mEffect);
|
||||
|
||||
this.CreateRenderTargets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unload's the graphics content required to draw light maps
|
||||
/// </summary>
|
||||
protected override void UnloadContent()
|
||||
{
|
||||
this.DisposeRenderTargets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates render targets
|
||||
/// </summary>
|
||||
private void CreateRenderTargets()
|
||||
{
|
||||
var targetWidth = GraphicsDevice.Viewport.Width / (int)(this.mLightMapSize);
|
||||
var targetHeight = GraphicsDevice.Viewport.Height / (int)(this.mLightMapSize);
|
||||
|
||||
this.mMap = new RenderTarget2D(GraphicsDevice, targetWidth, targetHeight, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8, 0, RenderTargetUsage.PlatformContents);
|
||||
this.mMapBlur = new RenderTarget2D(GraphicsDevice, targetWidth, targetHeight, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8, 0, RenderTargetUsage.PlatformContents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of render targets
|
||||
/// </summary>
|
||||
private void DisposeRenderTargets()
|
||||
{
|
||||
KryptonEngine.TryDispose(this.mMap);
|
||||
KryptonEngine.TryDispose(this.mMapBlur);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to dispose of disposable objects, and assigns them a null value afterward
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
private static void TryDispose(IDisposable obj)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
obj.Dispose();
|
||||
obj = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the light map to the current render target
|
||||
/// </summary>
|
||||
/// <param name="gameTime">N/A - Required</param>
|
||||
public override void Draw(GameTime gameTime)
|
||||
{
|
||||
this.LightMapPresent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the light map to be drawn (pre-render)
|
||||
/// </summary>
|
||||
public void LightMapPrepare()
|
||||
{
|
||||
// Prepare and set the matrix
|
||||
var viewWidth = this.GraphicsDevice.ScissorRectangle.Width;
|
||||
var viewHeight = this.GraphicsDevice.ScissorRectangle.Height;
|
||||
|
||||
// Prepare the matrix with optional settings and assign it to an effect parameter
|
||||
Matrix lightMapMatrix = this.LightmapMatrixGet();
|
||||
this.mEffect.Parameters["Matrix"].SetValue(lightMapMatrix);
|
||||
|
||||
// Obtain the original rendering states
|
||||
var originalRenderTargets = this.GraphicsDevice.GetRenderTargets();
|
||||
|
||||
// Set and clear the target
|
||||
this.GraphicsDevice.SetRenderTarget(this.mMap);
|
||||
this.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.Stencil, this.mAmbientColor, 0, 1);
|
||||
|
||||
// Make sure we're culling the right way!
|
||||
this.GraphicsDevice.RasterizerState = KryptonEngine.RasterizerStateGetFromCullMode(this.mCullMode);
|
||||
|
||||
// put the render target's size into a more friendly format
|
||||
var targetSize = new Vector2(this.mMap.Width, this.mMap.Height);
|
||||
|
||||
// Render Light Maps
|
||||
foreach (var light in this.mLights)
|
||||
{
|
||||
// Loop through each light within the view frustum
|
||||
if (light.Bounds.Intersects(this.mBounds))
|
||||
{
|
||||
// Clear the stencil and set the scissor rect (because we're stretching geometry past the light's reach)
|
||||
this.GraphicsDevice.Clear(ClearOptions.Stencil, Color.Black, 0, 1);
|
||||
this.GraphicsDevice.ScissorRectangle = KryptonEngine.ScissorRectCreateForLight(light, lightMapMatrix, targetSize);
|
||||
|
||||
// Draw the light!
|
||||
light.Draw(this.RenderHelper, this.mHulls);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.mBluriness > 0)
|
||||
{
|
||||
// Blur the shadow map horizontally to the blur target
|
||||
this.GraphicsDevice.SetRenderTarget(this.mMapBlur);
|
||||
this.RenderHelper.BlurTextureToTarget(this.mMap, LightMapSize.Full, BlurTechnique.Horizontal, this.mBluriness);
|
||||
|
||||
// Blur the shadow map vertically back to the final map
|
||||
this.GraphicsDevice.SetRenderTarget(this.mMap);
|
||||
this.RenderHelper.BlurTextureToTarget(this.mMapBlur, LightMapSize.Full, BlurTechnique.Vertical, this.mBluriness);
|
||||
}
|
||||
|
||||
// Reset to the original rendering states
|
||||
this.GraphicsDevice.SetRenderTargets(originalRenderTargets);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the final, modified matrix used to render the lightmap.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Matrix LightmapMatrixGet()
|
||||
{
|
||||
if (this.mSpriteBatchCompatabilityEnabled)
|
||||
{
|
||||
float xScale = (this.GraphicsDevice.Viewport.Width > 0) ? (1f / this.GraphicsDevice.Viewport.Width) : 0f;
|
||||
float yScale = (this.GraphicsDevice.Viewport.Height > 0) ? (-1f / this.GraphicsDevice.Viewport.Height) : 0f;
|
||||
|
||||
// This is the default matrix used to render sprites via spritebatch
|
||||
var matrixSpriteBatch = new Matrix()
|
||||
{
|
||||
M11 = xScale * 2f,
|
||||
M22 = yScale * 2f,
|
||||
M33 = 1f,
|
||||
M44 = 1f,
|
||||
M41 = -1f - xScale,
|
||||
M42 = 1f - yScale,
|
||||
};
|
||||
|
||||
// Return krypton's matrix, compensated for use with SpriteBatch
|
||||
return this.mWVP * matrixSpriteBatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return krypton's matrix
|
||||
return this.mWVP;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pixel-space rectangle which contains the light passed in
|
||||
/// </summary>
|
||||
/// <param name="light">The light used to create the rectangle</param>
|
||||
/// <param name="matrix">the WorldViewProjection matrix being used to render</param>
|
||||
/// <param name="targetSize">The rendertarget's size</param>
|
||||
/// <returns></returns>
|
||||
private static Rectangle ScissorRectCreateForLight(ILight2D light, Microsoft.Xna.Framework.Matrix matrix, Vector2 targetSize)
|
||||
{
|
||||
// This needs refining, but it works as is (I believe)
|
||||
var lightBounds = light.Bounds;
|
||||
|
||||
var min = KryptonEngine.VectorToPixel(lightBounds.Min, matrix, targetSize);
|
||||
var max = KryptonEngine.VectorToPixel(lightBounds.Max, matrix, targetSize);
|
||||
|
||||
var min2 = Vector2.Min(min, max);
|
||||
var max2 = Vector2.Max(min, max);
|
||||
|
||||
min = Vector2.Clamp(min2, Vector2.Zero, targetSize);
|
||||
max = Vector2.Clamp(max2, Vector2.Zero, targetSize);
|
||||
|
||||
return new Rectangle((int)(min.X), (int)(min.Y), (int)(max.X - min.X), (int)(max.Y - min.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a screen-space vector and puts it in to pixel space
|
||||
/// </summary>
|
||||
/// <param name="v"></param>
|
||||
/// <param name="matrix"></param>
|
||||
/// <param name="targetSize"></param>
|
||||
/// <returns></returns>
|
||||
private static Vector2 VectorToPixel(Vector2 v, Matrix matrix, Vector2 targetSize)
|
||||
{
|
||||
Vector2.Transform(ref v, ref matrix, out v);
|
||||
|
||||
v.X = (1 + v.X) * (targetSize.X / 2f);
|
||||
v.Y = (1 - v.Y) * (targetSize.Y / 2f);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a screen-space size vector and converts it to a pixel-space size vector
|
||||
/// </summary>
|
||||
/// <param name="v"></param>
|
||||
/// <param name="matrix"></param>
|
||||
/// <param name="targetSize"></param>
|
||||
/// <returns></returns>
|
||||
private static Vector2 ScaleToPixel(Vector2 v, Matrix matrix, Vector2 targetSize)
|
||||
{
|
||||
v.X *= matrix.M11 * (targetSize.X / 2f);
|
||||
v.Y *= matrix.M22 * (targetSize.Y / 2f);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a rasterize state by using the cull mode as a lookup
|
||||
/// </summary>
|
||||
/// <param name="cullMode">The cullmode used to lookup the rasterize state</param>
|
||||
/// <returns></returns>
|
||||
private static RasterizerState RasterizerStateGetFromCullMode(CullMode cullMode)
|
||||
{
|
||||
switch (cullMode)
|
||||
{
|
||||
case (CullMode.CullCounterClockwiseFace):
|
||||
return RasterizerState.CullCounterClockwise;
|
||||
|
||||
case (CullMode.CullClockwiseFace):
|
||||
return RasterizerState.CullClockwise;
|
||||
|
||||
default:
|
||||
return RasterizerState.CullNone;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Presents the light map to the current render target
|
||||
/// </summary>
|
||||
private void LightMapPresent()
|
||||
{
|
||||
RenderHelper.DrawTextureToTarget(this.mMap, this.mLightMapSize, BlendTechnique.Multiply);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+476
@@ -0,0 +1,476 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
using Krypton.Lights;
|
||||
|
||||
namespace Krypton
|
||||
{
|
||||
public enum BlendTechnique
|
||||
{
|
||||
Add = 1,
|
||||
Multiply = 2,
|
||||
};
|
||||
|
||||
public enum BlurTechnique
|
||||
{
|
||||
Horizontal = 1,
|
||||
Vertical = 2,
|
||||
};
|
||||
|
||||
public class KryptonRenderHelper
|
||||
{
|
||||
#region Static Unit Quad
|
||||
|
||||
private static VertexPositionTexture[] UnitQuad = new VertexPositionTexture[]
|
||||
{
|
||||
new VertexPositionTexture()
|
||||
{
|
||||
Position = new Vector3(-1, 1, 0),
|
||||
TextureCoordinate = new Vector2(0, 0),
|
||||
},
|
||||
new VertexPositionTexture()
|
||||
{
|
||||
Position = new Vector3(1, 1, 0),
|
||||
TextureCoordinate = new Vector2(1, 0),
|
||||
},
|
||||
new VertexPositionTexture()
|
||||
{
|
||||
Position = new Vector3(-1, -1, 0),
|
||||
TextureCoordinate = new Vector2(0, 1),
|
||||
},
|
||||
new VertexPositionTexture()
|
||||
{
|
||||
Position = new Vector3(1, -1, 0),
|
||||
TextureCoordinate = new Vector2(1, 1),
|
||||
},
|
||||
};
|
||||
|
||||
#endregion Static Unit Quad
|
||||
|
||||
private GraphicsDevice mGraphicsDevice;
|
||||
private Effect mEffect;
|
||||
private List<ShadowHullVertex> mShadowHullVertices = new List<ShadowHullVertex>();
|
||||
private List<Int32> mShadowHullIndicies = new List<Int32>();
|
||||
|
||||
public GraphicsDevice GraphicsDevice
|
||||
{
|
||||
get { return this.mGraphicsDevice; }
|
||||
}
|
||||
public Effect Effect
|
||||
{
|
||||
get { return this.mEffect; }
|
||||
}
|
||||
|
||||
public List<ShadowHullVertex> ShadowHullVertices
|
||||
{
|
||||
get { return this.mShadowHullVertices; }
|
||||
}
|
||||
public List<Int32> ShadowHullIndicies
|
||||
{
|
||||
get { return this.mShadowHullIndicies; }
|
||||
}
|
||||
|
||||
public KryptonRenderHelper(GraphicsDevice graphicsDevice, Effect effect)
|
||||
{
|
||||
this.mGraphicsDevice = graphicsDevice;
|
||||
this.mEffect = effect;
|
||||
}
|
||||
|
||||
public void BufferAddShadowHull(ShadowHull hull)
|
||||
{
|
||||
// Why do we need all of these again? (hint: we don't)
|
||||
|
||||
Matrix vertexMatrix = Matrix.Identity;
|
||||
Matrix normalMatrix = Matrix.Identity;
|
||||
|
||||
float cos, sin;
|
||||
|
||||
ShadowHullPoint point;
|
||||
ShadowHullVertex hullVertex;
|
||||
|
||||
// Create the matrices (3X speed boost versus prior version)
|
||||
cos = (float)Math.Cos(hull.Angle);
|
||||
sin = (float)Math.Sin(hull.Angle);
|
||||
|
||||
// vertexMatrix = scale * rotation * translation;
|
||||
vertexMatrix.M11 = hull.Scale.X * cos;
|
||||
vertexMatrix.M12 = hull.Scale.X * sin;
|
||||
vertexMatrix.M21 = hull.Scale.Y * -sin;
|
||||
vertexMatrix.M22 = hull.Scale.Y * cos;
|
||||
vertexMatrix.M41 = hull.Position.X;
|
||||
vertexMatrix.M42 = hull.Position.Y;
|
||||
|
||||
// normalMatrix = scaleInv * rotation;
|
||||
normalMatrix.M11 = (1f / hull.Scale.X) * cos;
|
||||
normalMatrix.M12 = (1f / hull.Scale.X) * sin;
|
||||
normalMatrix.M21 = (1f / hull.Scale.Y) * -sin;
|
||||
normalMatrix.M22 = (1f / hull.Scale.Y) * cos;
|
||||
|
||||
// Where are we in the buffer?
|
||||
var vertexCount = this.mShadowHullVertices.Count;
|
||||
|
||||
// Add the vertices to the buffer
|
||||
for (int i = 0; i < hull.NumPoints; i++)
|
||||
{
|
||||
|
||||
// Transform the vertices to screen coordinates
|
||||
point = hull.Points[i];
|
||||
Vector2.Transform(ref point.Position, ref vertexMatrix, out hullVertex.Position);
|
||||
Vector2.TransformNormal(ref point.Normal, ref normalMatrix, out hullVertex.Normal);
|
||||
|
||||
hullVertex.Color = Color.Black;
|
||||
|
||||
this.mShadowHullVertices.Add(hullVertex); // could this be sped up... ?
|
||||
}
|
||||
|
||||
//// Add the indicies to the buffer
|
||||
foreach (int index in hull.Indicies)
|
||||
{
|
||||
mShadowHullIndicies.Add(vertexCount + index); // what about this? Add range?
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSquareQuad(Vector2 position, float rotation, float size, Color color)
|
||||
{
|
||||
size /= 2;
|
||||
|
||||
size = (float)Math.Sqrt(Math.Pow(size, 2) + Math.Pow(size, 2));
|
||||
|
||||
rotation += (float)Math.PI / 4;
|
||||
|
||||
var cos = (float)Math.Cos(rotation) * size;
|
||||
var sin = (float)Math.Sin(rotation) * size;
|
||||
|
||||
var v1 = new Vector3(+cos, +sin, 0) + new Vector3(position, 0);
|
||||
var v2 = new Vector3(-sin, +cos, 0) + new Vector3(position, 0);
|
||||
var v3 = new Vector3(-cos, -sin, 0) + new Vector3(position, 0);
|
||||
var v4 = new Vector3(+sin, -cos, 0) + new Vector3(position, 0);
|
||||
|
||||
var quad = new VertexPositionColorTexture[]
|
||||
{
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = v2,
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(0,0),
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = v1,
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(1,0),
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = v3,
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(0,1),
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = v4,
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(1,1),
|
||||
},
|
||||
};
|
||||
|
||||
this.mGraphicsDevice.DrawUserPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleStrip, quad, 0, 2);
|
||||
}
|
||||
|
||||
public void DrawClippedFov(Vector2 position, float rotation, float size, Color color, float fov)
|
||||
{
|
||||
fov = MathHelper.Clamp(fov, 0, MathHelper.TwoPi);
|
||||
|
||||
if (fov == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (fov == MathHelper.TwoPi)
|
||||
{
|
||||
this.DrawSquareQuad(position, rotation, size, color);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ccw = ClampToBox(fov / 2);
|
||||
var cw = ClampToBox(-fov / 2);
|
||||
|
||||
var ccwTex = new Vector2(ccw.X + 1, -ccw.Y + 1) / 2f;
|
||||
var cwTex = new Vector2(cw.X + 1, -cw.Y + 1) / 2f;
|
||||
|
||||
VertexPositionColorTexture[] vertices;
|
||||
|
||||
#region Vertices
|
||||
vertices = new VertexPositionColorTexture[]
|
||||
{
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = Vector3.Zero,
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(0.5f, 0.5f),
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = new Vector3(ccw,0),
|
||||
Color = color,
|
||||
TextureCoordinate = ccwTex
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = new Vector3(-1, 1, 0),
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(0, 0),
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = new Vector3(1, 1, 0),
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(1, 0),
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = new Vector3(1, -1, 0),
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(1, 1),
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = new Vector3(-1, -1, 0),
|
||||
Color = color,
|
||||
TextureCoordinate = new Vector2(0, 1),
|
||||
},
|
||||
new VertexPositionColorTexture()
|
||||
{
|
||||
Position = new Vector3(cw, 0),
|
||||
Color = color,
|
||||
TextureCoordinate = cwTex,
|
||||
},
|
||||
};
|
||||
|
||||
var r = Matrix.CreateRotationZ(rotation) * Matrix.CreateScale(size / 2) * Matrix.CreateTranslation(new Vector3(position, 0));
|
||||
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
var vertex = vertices[i];
|
||||
|
||||
Vector3.Transform(ref vertex.Position, ref r, out vertex.Position);
|
||||
|
||||
vertices[i] = vertex;
|
||||
}
|
||||
|
||||
#endregion Vertices
|
||||
|
||||
Int32[] indicies;
|
||||
|
||||
#region Indicies
|
||||
|
||||
if (fov <= MathHelper.Pi / 2)
|
||||
{
|
||||
indicies = new Int32[]
|
||||
{
|
||||
0, 1, 6,
|
||||
};
|
||||
}
|
||||
else if (fov <= 3 * MathHelper.Pi / 2)
|
||||
{
|
||||
indicies = new Int32[]
|
||||
{
|
||||
0, 1, 3,
|
||||
0, 3, 4,
|
||||
0, 4, 6,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
indicies = new Int32[]
|
||||
{
|
||||
0, 1, 2,
|
||||
0, 2, 3,
|
||||
0, 3, 4,
|
||||
0, 4, 5,
|
||||
0, 5, 6,
|
||||
};
|
||||
}
|
||||
#endregion Indicies
|
||||
|
||||
this.mGraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indicies, 0, indicies.Length / 3);
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector2 ClampToBox(float angle)
|
||||
{
|
||||
var x = Math.Cos(angle);
|
||||
var y = Math.Sin(angle);
|
||||
var absMax = Math.Max(Math.Abs(x), Math.Abs(y));
|
||||
|
||||
return new Vector2((float)(x / absMax), (float)(y / absMax));
|
||||
}
|
||||
|
||||
public void BufferDraw()
|
||||
{
|
||||
if (this.mShadowHullIndicies.Count >= 3)
|
||||
{
|
||||
this.mGraphicsDevice.DrawUserIndexedPrimitives<ShadowHullVertex>(PrimitiveType.TriangleList, this.mShadowHullVertices.ToArray(), 0, this.mShadowHullVertices.Count, this.mShadowHullIndicies.ToArray(), 0, this.mShadowHullIndicies.Count / 3);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawFullscreenQuad()
|
||||
{
|
||||
// Obtain the original rendering states
|
||||
var originalRasterizerState = this.mGraphicsDevice.RasterizerState;
|
||||
|
||||
// Draw the quad
|
||||
this.mEffect.CurrentTechnique = this.mEffect.Techniques["ScreenCopy"];
|
||||
//this.mGraphicsDevice.RasterizerState = RasterizerState.CullNone;
|
||||
|
||||
this.mEffect.Parameters["TexelBias"].SetValue(new Vector2(0.5f / this.mGraphicsDevice.Viewport.Width, 0.5f / this.mGraphicsDevice.Viewport.Height));
|
||||
|
||||
foreach (var effectPass in this.mEffect.CurrentTechnique.Passes)
|
||||
{
|
||||
effectPass.Apply();
|
||||
this.mGraphicsDevice.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleStrip, KryptonRenderHelper.UnitQuad, 0, 2);
|
||||
}
|
||||
|
||||
// Reset to the original rendering states
|
||||
//this.mGraphicsDevice.RasterizerState = originalRasterizerState;
|
||||
}
|
||||
|
||||
public void BlurTextureToTarget(Texture2D texture, LightMapSize mapSize, BlurTechnique blurTechnique, float bluriness)
|
||||
{
|
||||
// Get the pass to use
|
||||
string passName = "";
|
||||
|
||||
switch (blurTechnique)
|
||||
{
|
||||
case (BlurTechnique.Horizontal):
|
||||
this.mEffect.Parameters["BlurFactorU"].SetValue(1f / this.GraphicsDevice.PresentationParameters.BackBufferWidth);
|
||||
passName = "HorizontalBlur";
|
||||
break;
|
||||
|
||||
case (BlurTechnique.Vertical):
|
||||
this.mEffect.Parameters["BlurFactorV"].SetValue(1f / this.mGraphicsDevice.PresentationParameters.BackBufferHeight);
|
||||
passName = "VerticalBlur";
|
||||
break;
|
||||
}
|
||||
|
||||
var biasFactor = KryptonRenderHelper.BiasFactorFromLightMapSize(mapSize);
|
||||
|
||||
// Calculate the texel bias
|
||||
Vector2 texelBias = new Vector2()
|
||||
{
|
||||
X = biasFactor / this.mGraphicsDevice.Viewport.Width,
|
||||
Y = biasFactor / this.mGraphicsDevice.Viewport.Height,
|
||||
};
|
||||
|
||||
|
||||
this.mEffect.Parameters["Texture0"].SetValue(texture);
|
||||
this.mEffect.Parameters["TexelBias"].SetValue(texelBias);
|
||||
this.mEffect.Parameters["Bluriness"].SetValue(bluriness);
|
||||
this.mEffect.CurrentTechnique = this.mEffect.Techniques["Blur"];
|
||||
|
||||
mEffect.CurrentTechnique.Passes[passName].Apply();
|
||||
this.mGraphicsDevice.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleStrip, KryptonRenderHelper.UnitQuad, 0, 2);
|
||||
}
|
||||
|
||||
public void DrawTextureToTarget(Texture2D texture, LightMapSize mapSize, BlendTechnique blend)
|
||||
{
|
||||
// Get the technique to use
|
||||
string techniqueName = "";
|
||||
|
||||
switch (blend)
|
||||
{
|
||||
case(BlendTechnique.Add):
|
||||
techniqueName = "TextureToTarget_Add";
|
||||
break;
|
||||
|
||||
case(BlendTechnique.Multiply):
|
||||
techniqueName = "TextureToTarget_Multiply";
|
||||
break;
|
||||
}
|
||||
|
||||
var biasFactor = KryptonRenderHelper.BiasFactorFromLightMapSize(mapSize);
|
||||
|
||||
// Calculate the texel bias
|
||||
Vector2 texelBias = new Vector2()
|
||||
{
|
||||
X = biasFactor / this.mGraphicsDevice.ScissorRectangle.Width,
|
||||
Y = biasFactor / this.mGraphicsDevice.ScissorRectangle.Height,
|
||||
};
|
||||
|
||||
this.mEffect.Parameters["Texture0"].SetValue(texture);
|
||||
this.mEffect.Parameters["TexelBias"].SetValue(texelBias);
|
||||
this.mEffect.CurrentTechnique = this.mEffect.Techniques[techniqueName];
|
||||
|
||||
// Draw the quad
|
||||
foreach (var effectPass in this.mEffect.CurrentTechnique.Passes)
|
||||
{
|
||||
effectPass.Apply();
|
||||
this.mGraphicsDevice.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleStrip, KryptonRenderHelper.UnitQuad, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
private static float BiasFactorFromLightMapSize(LightMapSize mapSize)
|
||||
{
|
||||
switch (mapSize)
|
||||
{
|
||||
case (LightMapSize.Full):
|
||||
return 0.5f;
|
||||
|
||||
case (LightMapSize.Fourth):
|
||||
return 0.6f;
|
||||
|
||||
case (LightMapSize.Eighth):
|
||||
return 0.7f;
|
||||
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void BufferAddBoundOutline(Common.BoundingRect boundingRect)
|
||||
{
|
||||
var vertexCount = this.mShadowHullVertices.Count;
|
||||
|
||||
this.mShadowHullVertices.Add(new ShadowHullVertex()
|
||||
{
|
||||
Color = Color.Black,
|
||||
Normal = Vector2.Zero,
|
||||
Position = new Vector2(boundingRect.Left, boundingRect.Top)
|
||||
});
|
||||
|
||||
this.mShadowHullVertices.Add(new ShadowHullVertex()
|
||||
{
|
||||
Color = Color.Black,
|
||||
Normal = Vector2.Zero,
|
||||
Position = new Vector2(boundingRect.Right, boundingRect.Top)
|
||||
});
|
||||
|
||||
this.mShadowHullVertices.Add(new ShadowHullVertex()
|
||||
{
|
||||
Color = Color.Black,
|
||||
Normal = Vector2.Zero,
|
||||
Position = new Vector2(boundingRect.Right, boundingRect.Bottom)
|
||||
});
|
||||
|
||||
this.mShadowHullVertices.Add(new ShadowHullVertex()
|
||||
{
|
||||
Color = Color.Black,
|
||||
Normal = Vector2.Zero,
|
||||
Position = new Vector2(boundingRect.Left, boundingRect.Bottom)
|
||||
});
|
||||
|
||||
this.mShadowHullIndicies.Add(vertexCount + 0);
|
||||
this.mShadowHullIndicies.Add(vertexCount + 1);
|
||||
this.mShadowHullIndicies.Add(vertexCount + 2);
|
||||
|
||||
this.mShadowHullIndicies.Add(vertexCount + 0);
|
||||
this.mShadowHullIndicies.Add(vertexCount + 2);
|
||||
this.mShadowHullIndicies.Add(vertexCount + 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+89
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Krypton
|
||||
{
|
||||
public static class LightTextureBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a Point Light based on a size.
|
||||
/// </summary>
|
||||
/// <param name="device">Your game's GraphicsDevice</param>
|
||||
/// <param name="size">Maximum Size</param>
|
||||
/// <returns>Light Texture</returns>
|
||||
public static Texture2D CreatePointLight(GraphicsDevice device, int size)
|
||||
{
|
||||
return CreateConicLight(device, size, MathHelper.TwoPi, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Conic Light based on the size, and field of view.
|
||||
/// </summary>
|
||||
/// <param name="device">Your game's GraphicsDevice</param>
|
||||
/// <param name="size">Maximum Size</param>
|
||||
/// <param name="FOV">Maximum Field of View</param>
|
||||
/// <returns>Light Texture</returns>
|
||||
public static Texture2D CreateConicLight(GraphicsDevice device, int size, float FOV)
|
||||
{
|
||||
return CreateConicLight(device, size, FOV, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Conic Light based on the size, field of view, and near plane distance.
|
||||
/// </summary>
|
||||
/// <param name="device">Your game's GraphicsDevice</param>
|
||||
/// <param name="size">Maximum size</param>
|
||||
/// <param name="FOV">Maximum Field of View</param>
|
||||
/// <param name="nearPlaneDistance">Prevents texture from being drawn at this plane distance, originating from the center of light</param>
|
||||
/// <returns>Light Texture</returns>
|
||||
public static Texture2D CreateConicLight(GraphicsDevice device, int size, float FOV, float nearPlaneDistance)
|
||||
{
|
||||
/*if (!IsPowerOfTwo(size))
|
||||
throw new Exception("The size must be a power of 2");*/
|
||||
float[,] Data = new float[size, size];
|
||||
|
||||
float center = size / 2;
|
||||
|
||||
FOV = FOV / 2;
|
||||
|
||||
for (int x = 0; x < size; x++)
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
float Distance = Vector2.Distance(new Vector2(x, y), new Vector2(center));
|
||||
|
||||
Vector2 Difference = new Vector2(x, y) - new Vector2(center);
|
||||
float Angle = (float)Math.Atan2(Difference.Y, Difference.X);
|
||||
|
||||
if (Distance <= center && Distance >= nearPlaneDistance && Math.Abs(Angle) <= FOV)
|
||||
Data[x, y] = (center - Distance) / center;
|
||||
else
|
||||
Data[x, y] = 0;
|
||||
}
|
||||
|
||||
Texture2D tex = new Texture2D(device, size, size);
|
||||
|
||||
Color[] Data1D = new Color[size * size];
|
||||
for (int x = 0; x < size; x++)
|
||||
for (int y = 0; y < size; y++)
|
||||
Data1D[x + y * size] = new Color(new Vector3(Data[x, y]));
|
||||
|
||||
tex.SetData<Color>(Data1D);
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Math helper to determine if integer is a power of two
|
||||
/// </summary>
|
||||
/// <param name="x">Integer value</param>
|
||||
private static bool IsPowerOfTwo(int x)
|
||||
{
|
||||
return (x & (x - 1)) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
using Krypton.Common;
|
||||
|
||||
namespace Krypton.Lights
|
||||
{
|
||||
public interface ILight2D
|
||||
{
|
||||
BoundingRect Bounds { get; }
|
||||
|
||||
void Draw(KryptonRenderHelper renderHelper, List<ShadowHull> hulls);
|
||||
}
|
||||
}
|
||||
Executable
+192
@@ -0,0 +1,192 @@
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// The light's position
|
||||
/// </summary>
|
||||
public Vector2 Position
|
||||
{
|
||||
get { return this.mPosition; }
|
||||
set { this.mPosition = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The X coordinate of the light's position
|
||||
/// </summary>
|
||||
public float X
|
||||
{
|
||||
get { return this.mPosition.X; }
|
||||
set { this.mPosition.X = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Y coordinate of the light's position
|
||||
/// </summary>
|
||||
public float Y
|
||||
{
|
||||
get { return this.mPosition.Y; }
|
||||
set { this.mPosition.Y = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The light's angle
|
||||
/// </summary>
|
||||
public float Angle
|
||||
{
|
||||
get { return this.mAngle; }
|
||||
set { this.mAngle = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The texture used as the base light map, from which shadows will be subtracted
|
||||
/// </summary>
|
||||
public Texture2D Texture
|
||||
{
|
||||
get { return this.mTexture; }
|
||||
set { this.mTexture = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color used to tint the light's texture
|
||||
/// </summary>
|
||||
public Color Color
|
||||
{
|
||||
get { return this.mColor; }
|
||||
set { this.mColor = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The light's maximum radius (or "half width", if you will)
|
||||
/// </summary>
|
||||
public float Range
|
||||
{
|
||||
get { return this.mRange; }
|
||||
set { this.mRange = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the light's field of view. This value determines the angles at which the light will cease to draw
|
||||
/// </summary>
|
||||
public float Fov
|
||||
{
|
||||
get { return this.mFov; }
|
||||
set
|
||||
{
|
||||
this.mFov = MathHelper.Clamp(value, 0, MathHelper.TwoPi);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the light's intensity. Think pixel = (tex * color) ^ (1 / intensity)
|
||||
/// </summary>
|
||||
public float Intensity
|
||||
{
|
||||
get { return this.mIntensity; }
|
||||
set { this.mIntensity = MathHelper.Clamp(value, 0.01f, 3f); }
|
||||
}
|
||||
|
||||
#endregion Parameters
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating weither or not to draw the light
|
||||
/// </summary>
|
||||
public bool IsOn
|
||||
{
|
||||
get { return this.mIsOn; }
|
||||
set { this.mIsOn = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws shadows from the light's position outward
|
||||
/// </summary>
|
||||
/// <param name="helper">A render helper for drawing shadows</param>
|
||||
/// <param name="hulls">The shadow hulls used to draw shadows</param>
|
||||
public void Draw(KryptonRenderHelper helper, List<ShadowHull> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a vector's length is less than a specified value
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset</param>
|
||||
/// <param name="dist">Distance</param>
|
||||
/// <returns></returns>
|
||||
private static bool IsInRange(Vector2 offset, float dist)
|
||||
{
|
||||
if (offset.X * offset.X + offset.Y * offset.Y < dist * dist)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world-space bounds which contain the light
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Krypton")]
|
||||
[assembly: AssemblyProduct("Krypton")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type. Only Windows
|
||||
// assemblies support COM.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// On Windows, the following GUID is for the ID of the typelib if this
|
||||
// project is exposed to COM. On other platforms, it unique identifies the
|
||||
// title storage container when deploying this assembly to the device.
|
||||
[assembly: Guid("0ce98116-fee5-4948-bdc1-edab3ed0d43e")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
Executable
+241
@@ -0,0 +1,241 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// A hull used for casting shadows from a light source
|
||||
/// </summary>
|
||||
public class ShadowHull
|
||||
{
|
||||
#region Orientation
|
||||
|
||||
/// <summary>
|
||||
/// The position of the shadow hull
|
||||
/// </summary>
|
||||
public Vector2 Position;
|
||||
|
||||
/// <summary>
|
||||
/// The angle of the shadow hull
|
||||
/// </summary>
|
||||
public float Angle;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Shape
|
||||
|
||||
/// <summary>
|
||||
/// The maximum radius in which all of the shadow hull's vertices are contained, originating from the hull's position
|
||||
/// </summary>
|
||||
public float MaxRadius;
|
||||
|
||||
/// <summary>
|
||||
/// The vertices comprising the shadow hull
|
||||
/// </summary>
|
||||
public ShadowHullPoint[] Points;
|
||||
|
||||
/// <summary>
|
||||
/// The number of vertices comprising the shadow hull
|
||||
/// </summary>
|
||||
public int NumPoints;
|
||||
|
||||
/// <summary>
|
||||
/// The indicies used to render the shadow hull
|
||||
/// </summary>
|
||||
public Int32[] Indicies;
|
||||
|
||||
/// <summary>
|
||||
/// The number of indicies used to render the shadow hull
|
||||
/// </summary>
|
||||
public int NumIndicies;
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating if the hull should cast a shadow
|
||||
/// </summary>
|
||||
public bool Visible = true;
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating how much to stretch the hull
|
||||
/// </summary>
|
||||
public Vector2 Scale = Vector2.One;
|
||||
|
||||
#endregion
|
||||
|
||||
private ShadowHull(){}
|
||||
|
||||
#region Factory Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a rectangular shadow hull
|
||||
/// </summary>
|
||||
/// <param name="size">The dimensions of the rectangle</param>
|
||||
/// <returns>A rectangular shadow hull</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a circular shadow hull
|
||||
/// </summary>
|
||||
/// <param name="radius">radius of the circle</param>
|
||||
/// <param name="sides">number of sides the circle will be comprised of</param>
|
||||
/// <returns>A circular shadow hull</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a custom shadow hull based on a series of vertices
|
||||
/// </summary>
|
||||
/// <param name="points">The points which the shadow hull will be comprised of</param>
|
||||
/// <returns>A custom shadow hulll</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Krypton
|
||||
{
|
||||
/// <summary>
|
||||
/// A two dimensional point with normal
|
||||
/// </summary>
|
||||
public struct ShadowHullPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new Shadow Hull Point
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the point</param>
|
||||
/// <param name="normal">The normal of the side which the point helps represent</param>
|
||||
public ShadowHullPoint(Vector2 position, Vector2 normal)
|
||||
{
|
||||
this.Position = position;
|
||||
this.Normal = normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the point
|
||||
/// </summary>
|
||||
public Vector2 Position;
|
||||
|
||||
/// <summary>
|
||||
/// The normal of the side of the shadow hull of which the point helps represent
|
||||
/// </summary>
|
||||
public Vector2 Normal;
|
||||
}
|
||||
}
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Krypton
|
||||
{
|
||||
public struct ShadowHullVertex : IVertexType
|
||||
{
|
||||
/// <summary>
|
||||
/// The position of the vertex
|
||||
/// </summary>
|
||||
public Vector2 Position;
|
||||
|
||||
/// <summary>
|
||||
/// The normal of the vertex
|
||||
/// </summary>
|
||||
public Vector2 Normal;
|
||||
|
||||
/// <summary>
|
||||
/// The color of vertex
|
||||
/// </summary>
|
||||
public Color Color;
|
||||
|
||||
private static readonly VertexDeclaration mVertexDeclaration;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public VertexDeclaration VertexDeclaration { get { return ShadowHullVertex.mVertexDeclaration; } }
|
||||
|
||||
static ShadowHullVertex()
|
||||
{
|
||||
VertexElement[] elements = new VertexElement[]
|
||||
{
|
||||
new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0),
|
||||
new VertexElement(8, VertexElementFormat.Vector2, VertexElementUsage.Normal,0),
|
||||
new VertexElement(16, VertexElementFormat.Color, VertexElementUsage.Color,0),
|
||||
};
|
||||
|
||||
mVertexDeclaration = new VertexDeclaration(elements);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="normal"></param>
|
||||
/// <param name="opacity"></param>
|
||||
public ShadowHullVertex(Vector2 position, Vector2 normal, Color color)
|
||||
{
|
||||
this.Position = position;
|
||||
this.Normal = normal;
|
||||
this.Color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user