/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw( GameTime gameTime )
{
// As usual, clear the backbuffer (or the current render target).
GraphicsDevice.Clear( Color.CornflowerBlue );
// Create the array of bone transforms for the floor and populate it.
ModelBone[] transforms = new ModelBone[ this.floor.Bones.Count ];
this.floor.Bones.CopyTo( transforms, 0 );
// For each mesh in the floor model.
foreach(var mesh in this.floor.Meshes)
{
// Get the basic effect.
foreach ( BasicEffect effect in mesh.Effects )
{
// Set values and commit changes.
effect.DiffuseColor = Color.LightSteelBlue.ToVector3();
effect.View = this.avatarRenderer.View;
effect.Projection = this.avatarRenderer.Projection;
effect.World = transforms[ mesh.ParentBone.Index ].Transform;
effect.CommitChanges();
}
// Finally, draw the mesh.
mesh.Draw();
}
// Can we draw the avatar?
if ( avatarRenderer != null && currentAnimation != null )
{
// If we can, is the animation in transition?
if ( this.isInTransition )
{
// If so, draw it with the interpolated transforms.
this.avatarRenderer.Draw(
this.transitionTransforms,
currentAnimation.Expression );
}
else
{
// If not, draw it with the actual transforms.
this.avatarRenderer.Draw(
this.currentAnimation.BoneTransforms,
currentAnimation.Expression );
}
// Make the light sources of the avatar dark.
Vector3 ambientColor = this.avatarRenderer.AmbientLightColor;
Vector3 lightColor = this.avatarRenderer.LightColor;
this.avatarRenderer.AmbientLightColor =
this.avatarRenderer.LightColor =
-10 * Vector3.One;
// Enable alpha blending.
GraphicsDevice.RenderState.AlphaBlendEnable = true;
GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
// Change the depth bias just a bit to avoid z-fighting.
float sourceDepthBias = GraphicsDevice.RenderState.DepthBias;
GraphicsDevice.RenderState.DepthBias = -0.0001f;
// Set the new light direction.
this.avatarRenderer.LightDirection = Vector3.Normalize(
Vector3.Right * 7.5f * (float)Math.Cos( lightRotation ) +
Vector3.Forward * 15.0f * (float)Math.Sin( lightRotation ) +
Vector3.Up * 10.0f );
// If the avatar is stepping over the floor, then move the plane
// according to the "altitude" of the avatar in the world so as
// to calculate and cast shadows in the correct world position
// (also, take into account that in case of a "jump" movement, in a
// "complete" shadow system you must reposition the shadow along the
// floor taking into account the place where the light-ray hits the
// floor while it points to the avatar; otherwise, it will stand still
// as if the avatar never jumped in the first place).
this.plane.D = -this.avatarRenderer.World.Translation.Y;
// Calculate and set the world transform that will flatten the
// avatar's geometry, taking into account the original rotation,
// scale and translation factors.
Matrix world = this.avatarRenderer.World;
this.avatarRenderer.World *= Matrix.CreateShadow(
this.avatarRenderer.LightDirection,
this.plane );
// Is the animation in transition?
if ( this.isInTransition )
{
// If so, draw it with the interpolated transforms.
this.avatarRenderer.Draw(
this.transitionTransforms,
currentAnimation.Expression );
}
else
{
// If not, draw it with the actual transforms.
this.avatarRenderer.Draw(
this.currentAnimation.BoneTransforms,
currentAnimation.Expression );
}
// Reset all affected values.
this.avatarRenderer.World = world;
this.avatarRenderer.AmbientLightColor = ambientColor;
this.avatarRenderer.LightColor = lightColor;
GraphicsDevice.RenderState.DepthBias = sourceDepthBias;
GraphicsDevice.RenderState.AlphaBlendEnable = false;
}
// The following is used to show some statistics and other info
// on screen. It can be omitted (or optimized).
this.spriteBatch.Begin();
// No need for further explanation.
this.spriteBatch.DrawString(
this.font,
"Press 'A' to force changing animations or 'Back' to exit.",
new Vector2( 50, 25 ),
Color.White );
// No need for further explanation.
this.spriteBatch.DrawString(
this.font,
"Press 'B' to change the type of selection : " +
( this.moveRandomly ? "RANDOMLY" : "IN ASCENDING ORDER" )
+ ".",
new Vector2( 50, 55 ),
Color.White );
// Draw the animation pointer, whether we are processing a transition and
// the current transition time. Please notice that in this implementation
// when the current animation is about to end (that is, 1 second or less),
// the pointer "currentAnimationId" will change even if the animation is still
// the same, so you will see a different number and name during 1 second or so.
this.spriteBatch.DrawString(
this.font,
this.currentAnimationId + " : " +
( (AvatarAnimationPreset)this.currentAnimationId ).ToString() +
" (" +
( !this.isInTransition ? "no transition" : this.transitionProgress.ToString() + " processed" ) +
").",
new Vector2( 50, 85 ),
Color.White );
// Draw the current position and length of the animation being rendered.
if ( currentAnimation != null )
{
this.spriteBatch.DrawString(
this.font,
"Processed " +
this.currentAnimation.CurrentPosition.ToString() +
" of " +
this.currentAnimation.Length.ToString() +
".",
new Vector2( 50, 115 ),
Color.White );
}
// Flush the batch.
this.spriteBatch.End();
// As usual, call the base method.
base.Draw( gameTime );
}