public
class Lens : DrawableGameComponent
{
float glowSize =
400;
float querySize =
100;
public Matrix View;
public Matrix Projection;
public Vector3 LightDirection = Vector3.Normalize(new Vector3(-1, -0.1f, 0.3f));
// Graphics objects.
Texture2D glowSprite;
SpriteBatch spriteBatch;
BasicEffect basicEffect;
VertexDeclaration vertexDeclaration;
VertexPositionColor[] queryVertices;
// An occlusion query is used to detect when the sun is hidden behind scenery.
OcclusionQuery occlusionQuery;
bool occlusionQueryActive;
float occlusionAlpha;
Flare[] flares =
{
new Flare(-0.5f, 0.7f, new Color( 50, 25, 50), "flare1"),
new Flare( 0.3f, 0.4f, new Color(100, 255, 200), "flare1"),
new Flare( 1.2f, 1.0f, new Color(100, 50, 50), "flare1"),
new Flare( 1.5f, 1.5f, new Color( 50, 100, 50), "flare1"),
new Flare(-0.3f, 0.7f, new Color(200, 50, 50), "flare2"),
new Flare( 0.6f, 0.9f, new Color( 50, 100, 50), "flare2"),
new Flare( 0.7f, 0.4f, new Color( 50, 200, 200), "flare2"),
new Flare(-0.7f, 0.7f, new Color( 50, 100, 25), "flare3"),
new Flare( 0.0f, 0.6f, new Color( 25, 25, 25), "flare3"),
new Flare( 2.0f, 1.4f, new Color( 25, 50, 100), "flare3"),
};
Camera camera;
public Lens(Game game, Camera camera): base(game)
{
this.camera = camera;
}
protected
override
void LoadContent()
{
spriteBatch =
new SpriteBatch(GraphicsDevice);
glowSprite = Game.Content.Load<Texture2D>("glow");
foreach (Flare flare in flares)
{
flare.Texture = Game.Content.Load<Texture2D>(flare.TextureName);
}
basicEffect =
new BasicEffect(GraphicsDevice, null);
basicEffect.View = Matrix.Identity;
basicEffect.VertexColorEnabled =
true;
vertexDeclaration =
new VertexDeclaration(GraphicsDevice,VertexPositionColor.VertexElements);
// Create vertex data for the occlusion query polygons.
queryVertices =
new VertexPositionColor[4];
queryVertices[0].Position =
new Vector3(-querySize /
2, -querySize /
2, -1);
queryVertices[1].Position =
new Vector3(querySize /
2, -querySize /
2, -1);
queryVertices[2].Position =
new Vector3(querySize /
2, querySize /
2, -1);
queryVertices[3].Position =
new Vector3(-querySize /
2, querySize /
2, -1);
// Create the occlusion query object.
occlusionQuery =
new OcclusionQuery(GraphicsDevice);
base.LoadContent();
}
public
override
void Update(GameTime gameTime)
{
Projection = camera.Projection;
base.Update(gameTime);
}
#region Draw
public
override
void Draw(GameTime gameTime)
{
Matrix infiniteView = View;
infiniteView.Translation = Vector3.Zero;
// Project the light position into 2D screen space.
Viewport viewport = GraphicsDevice.Viewport;
Vector3 projectedPosition = viewport.Project(-LightDirection, Projection, infiniteView, Matrix.Identity);
if ((projectedPosition.Z <
0) || (projectedPosition.Z >
1))
return;
Vector2 lightPosition =
new Vector2(projectedPosition.X, projectedPosition.Y);
UpdateOcclusion(lightPosition);
// If it is visible, draw the flare effect.
if (occlusionAlpha >
0)
{
DrawGlow(lightPosition);
DrawFlares(lightPosition);
}
RestoreRenderStates();
}
void UpdateOcclusion(Vector2 lightPosition)
{
if (!occlusionQuery.IsSupported)
return;
if (occlusionQueryActive)
{
if (!occlusionQuery.IsComplete)
return;
const
float queryArea = querySize * querySize;
occlusionAlpha = Math.Min(occlusionQuery.PixelCount / queryArea, 1);
}
RenderState renderState = GraphicsDevice.RenderState;
renderState.DepthBufferEnable =
true;
renderState.DepthBufferWriteEnable =
false;
renderState.AlphaTestEnable =
false;
renderState.ColorWriteChannels = ColorWriteChannels.None;
Viewport viewport = GraphicsDevice.Viewport;
basicEffect.World = Matrix.CreateTranslation(lightPosition.X, lightPosition.Y, 0);
basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);
basicEffect.Begin();
basicEffect.CurrentTechnique.Passes[0].Begin();
GraphicsDevice.VertexDeclaration = vertexDeclaration;
occlusionQuery.Begin();
GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleFan, queryVertices, 0, 2);
occlusionQuery.End();
basicEffect.CurrentTechnique.Passes[0].End();
basicEffect.End();
// Reset renderstates.
renderState.ColorWriteChannels = ColorWriteChannels.All;
renderState.DepthBufferWriteEnable =
true;
occlusionQueryActive =
true;
}
void DrawGlow(Vector2 lightPosition)
{
Vector4 color =
new Vector4(1, 1, 1, occlusionAlpha);
Vector2 origin =
new Vector2(glowSprite.Width, glowSprite.Height) /
2;
float scale = glowSize *
2
/ glowSprite.Width;
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
spriteBatch.Draw(glowSprite, lightPosition, null, new Color(color), 0,origin, scale, SpriteEffects.None, 0);
spriteBatch.End();
}
void DrawFlares(Vector2 lightPosition)
{
Viewport viewport = GraphicsDevice.Viewport;
Vector2 screenCenter =
new Vector2(viewport.Width, viewport.Height) /
2;
Vector2 flareVector = screenCenter - lightPosition;
spriteBatch.Begin(SpriteBlendMode.Additive);
foreach (Flare flare in flares)
{
Vector2 flarePosition = lightPosition + flareVector * flare.Position;
Vector4 flareColor = flare.Color.ToVector4();
flareColor.W *= occlusionAlpha;
Vector2 flareOrigin =
new Vector2(flare.Texture.Width,flare.Texture.Height) /
2;
spriteBatch.Draw(flare.Texture, flarePosition, null,new Color(flareColor), 1, flareOrigin, flare.Scale, SpriteEffects.None, 0);
}
spriteBatch.End();
}
#endregion
}