#region File Description
#endregion
#region Using Statements
using
System;
#endregion
namespace
Microsoft.Xna.Framework.Graphics
{
/// <summary>
/// Built-in effect for rendering skinned character models.
/// </summary>
public
class
SkinnedEffect : Effect, IEffectMatrices, IEffectLights, IEffectFog
{
public
const
int
MaxBones = 72;
#region Effect Parameters
EffectParameter textureParam;
EffectParameter diffuseColorParam;
EffectParameter emissiveColorParam;
EffectParameter specularColorParam;
EffectParameter specularPowerParam;
EffectParameter eyePositionParam;
EffectParameter fogColorParam;
EffectParameter fogVectorParam;
EffectParameter worldParam;
EffectParameter worldInverseTransposeParam;
EffectParameter worldViewProjParam;
EffectParameter bonesParam;
EffectParameter shaderIndexParam;
#endregion
#region Fields
bool
preferPerPixelLighting;
bool
oneLight;
bool
fogEnabled;
Matrix world = Matrix.Identity;
Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity;
Matrix worldView;
Vector3 diffuseColor = Vector3.One;
Vector3 emissiveColor = Vector3.Zero;
Vector3 ambientLightColor = Vector3.Zero;
float
alpha = 1;
DirectionalLight light0;
DirectionalLight light1;
DirectionalLight light2;
float
fogStart = 0;
float
fogEnd = 1;
int
weightsPerVertex = 4;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
#endregion
#region Public Properties
/// <summary>
/// Gets or sets the world matrix.
/// </summary>
public
Matrix World
{
get
{
return
world; }
set
{
world = value;
dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
}
}
/// <summary>
/// Gets or sets the view matrix.
/// </summary>
public
Matrix View
{
get
{
return
view; }
set
{
view = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.EyePosition | EffectDirtyFlags.Fog;
}
}
/// <summary>
/// Gets or sets the projection matrix.
/// </summary>
public
Matrix Projection
{
get
{
return
projection; }
set
{
projection = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
/// <summary>
/// Gets or sets the material diffuse color (range 0 to 1).
/// </summary>
public
Vector3 DiffuseColor
{
get
{
return
diffuseColor; }
set
{
diffuseColor = value;
dirtyFlags |= EffectDirtyFlags.MaterialColor;
}
}
/// <summary>
/// Gets or sets the material emissive color (range 0 to 1).
/// </summary>
public
Vector3 EmissiveColor
{
get
{
return
emissiveColor; }
set
{
emissiveColor = value;
dirtyFlags |= EffectDirtyFlags.MaterialColor;
}
}
/// <summary>
/// Gets or sets the material specular color (range 0 to 1).
/// </summary>
public
Vector3 SpecularColor
{
get
{
return
specularColorParam.GetValueVector3(); }
set
{ specularColorParam.SetValue(value); }
}
/// <summary>
/// Gets or sets the material specular power.
/// </summary>
public
float
SpecularPower
{
get
{
return
specularPowerParam.GetValueSingle(); }
set
{ specularPowerParam.SetValue(value); }
}
/// <summary>
/// Gets or sets the material alpha.
/// </summary>
public
float
Alpha
{
get
{
return
alpha; }
set
{
alpha = value;
dirtyFlags |= EffectDirtyFlags.MaterialColor;
}
}
/// <summary>
/// Gets or sets the per-pixel lighting prefer flag.
/// </summary>
public
bool
PreferPerPixelLighting
{
get
{
return
preferPerPixelLighting; }
set
{
if
(preferPerPixelLighting != value)
{
preferPerPixelLighting = value;
dirtyFlags |= EffectDirtyFlags.ShaderIndex;
}
}
}
/// <summary>
/// Gets or sets the ambient light color (range 0 to 1).
/// </summary>
public
Vector3 AmbientLightColor
{
get
{
return
ambientLightColor; }
set
{
ambientLightColor = value;
dirtyFlags |= EffectDirtyFlags.MaterialColor;
}
}
/// <summary>
/// Gets the first directional light.
/// </summary>
public
DirectionalLight DirectionalLight0 {
get
{
return
light0; } }
/// <summary>
/// Gets the second directional light.
/// </summary>
public
DirectionalLight DirectionalLight1 {
get
{
return
light1; } }
/// <summary>
/// Gets the third directional light.
/// </summary>
public
DirectionalLight DirectionalLight2 {
get
{
return
light2; } }
/// <summary>
/// Gets or sets the fog enable flag.
/// </summary>
public
bool
FogEnabled
{
get
{
return
fogEnabled; }
set
{
if
(fogEnabled != value)
{
fogEnabled = value;
dirtyFlags |= EffectDirtyFlags.ShaderIndex | EffectDirtyFlags.FogEnable;
}
}
}
/// <summary>
/// Gets or sets the fog start distance.
/// </summary>
public
float
FogStart
{
get
{
return
fogStart; }
set
{
fogStart = value;
dirtyFlags |= EffectDirtyFlags.Fog;
}
}
/// <summary>
/// Gets or sets the fog end distance.
/// </summary>
public
float
FogEnd
{
get
{
return
fogEnd; }
set
{
fogEnd = value;
dirtyFlags |= EffectDirtyFlags.Fog;
}
}
/// <summary>
/// Gets or sets the fog color.
/// </summary>
public
Vector3 FogColor
{
get
{
return
fogColorParam.GetValueVector3(); }
set
{ fogColorParam.SetValue(value); }
}
/// <summary>
/// Gets or sets the current texture.
/// </summary>
public
Texture2D Texture
{
get
{
return
textureParam.GetValueTexture2D(); }
set
{ textureParam.SetValue(value); }
}
/// <summary>
/// Gets or sets the number of skinning weights to evaluate for each vertex (1, 2, or 4).
/// </summary>
public
int
WeightsPerVertex
{
get
{
return
weightsPerVertex; }
set
{
if
((value != 1) &&
(value != 2) &&
(value != 4))
{
throw
new
ArgumentOutOfRangeException(
"value"
);
}
weightsPerVertex = value;
dirtyFlags |= EffectDirtyFlags.ShaderIndex;
}
}
/// <summary>
/// Sets an array of skinning bone transform matrices.
/// </summary>
public
void
SetBoneTransforms(Matrix[] boneTransforms)
{
if
((boneTransforms ==
null
) || (boneTransforms.Length == 0))
throw
new
ArgumentNullException(
"boneTransforms"
);
if
(boneTransforms.Length > MaxBones)
throw
new
ArgumentException();
bonesParam.SetValue(boneTransforms);
}
/// <summary>
/// Gets a copy of the current skinning bone transform matrices.
/// </summary>
public
Matrix[] GetBoneTransforms(
int
count)
{
if
(count <= 0 || count > MaxBones)
throw
new
ArgumentOutOfRangeException(
"count"
);
Matrix[] bones = bonesParam.GetValueMatrixArray(count);
for
(
int
i = 0; i < bones.Length; i++)
{
bones[i].M44 = 1;
}
return
bones;
}
/// <summary>
/// This effect requires lighting, so we explicitly implement
/// IEffectLights.LightingEnabled, and do not allow turning it off.
/// </summary>
bool
IEffectLights.LightingEnabled
{
get
{
return
true
; }
set
{
if
(!value)
throw
new
NotSupportedException(
"SkinnedEffect does not support setting LightingEnabled to false."
); }
}
#endregion
#region Methods
/// <summary>
/// Creates a new SkinnedEffect with default parameter settings.
/// </summary>
public
SkinnedEffect(GraphicsDevice device)
:
base
(device, Resources.SkinnedEffect)
{
CacheEffectParameters(
null
);
DirectionalLight0.Enabled =
true
;
SpecularColor = Vector3.One;
SpecularPower = 16;
Matrix[] identityBones =
new
Matrix[MaxBones];
for
(
int
i = 0; i < MaxBones; i++)
{
identityBones[i] = Matrix.Identity;
}
SetBoneTransforms(identityBones);
}
/// <summary>
/// Creates a new SkinnedEffect by cloning parameter settings from an existing instance.
/// </summary>
protected
SkinnedEffect(SkinnedEffect cloneSource)
:
base
(cloneSource)
{
CacheEffectParameters(cloneSource);
preferPerPixelLighting = cloneSource.preferPerPixelLighting;
fogEnabled = cloneSource.fogEnabled;
world = cloneSource.world;
view = cloneSource.view;
projection = cloneSource.projection;
diffuseColor = cloneSource.diffuseColor;
emissiveColor = cloneSource.emissiveColor;
ambientLightColor = cloneSource.ambientLightColor;
alpha = cloneSource.alpha;
fogStart = cloneSource.fogStart;
fogEnd = cloneSource.fogEnd;
weightsPerVertex = cloneSource.weightsPerVertex;
}
/// <summary>
/// Creates a clone of the current SkinnedEffect instance.
/// </summary>
public
override
Effect Clone()
{
return
new
SkinnedEffect(
this
);
}
/// <summary>
/// Sets up the standard key/fill/back lighting rig.
/// </summary>
public
void
EnableDefaultLighting()
{
AmbientLightColor = EffectHelpers.EnableDefaultLighting(light0, light1, light2);
}
/// <summary>
/// Looks up shortcut references to our effect parameters.
/// </summary>
void
CacheEffectParameters(SkinnedEffect cloneSource)
{
textureParam = Parameters[
"Texture"
];
diffuseColorParam = Parameters[
"DiffuseColor"
];
emissiveColorParam = Parameters[
"EmissiveColor"
];
specularColorParam = Parameters[
"SpecularColor"
];
specularPowerParam = Parameters[
"SpecularPower"
];
eyePositionParam = Parameters[
"EyePosition"
];
fogColorParam = Parameters[
"FogColor"
];
fogVectorParam = Parameters[
"FogVector"
];
worldParam = Parameters[
"World"
];
worldInverseTransposeParam = Parameters[
"WorldInverseTranspose"
];
worldViewProjParam = Parameters[
"WorldViewProj"
];
bonesParam = Parameters[
"Bones"
];
shaderIndexParam = Parameters[
"ShaderIndex"
];
light0 =
new
DirectionalLight(Parameters[
"DirLight0Direction"
],
Parameters[
"DirLight0DiffuseColor"
],
Parameters[
"DirLight0SpecularColor"
],
(cloneSource !=
null
) ? cloneSource.light0 :
null
);
light1 =
new
DirectionalLight(Parameters[
"DirLight1Direction"
],
Parameters[
"DirLight1DiffuseColor"
],
Parameters[
"DirLight1SpecularColor"
],
(cloneSource !=
null
) ? cloneSource.light1 :
null
);
light2 =
new
DirectionalLight(Parameters[
"DirLight2Direction"
],
Parameters[
"DirLight2DiffuseColor"
],
Parameters[
"DirLight2SpecularColor"
],
(cloneSource !=
null
) ? cloneSource.light2 :
null
);
}
/// <summary>
/// Lazily computes derived parameter values immediately before applying the effect.
/// </summary>
protected
internal
override
void
OnApply()
{
dirtyFlags = EffectHelpers.SetWorldViewProjAndFog(dirtyFlags,
ref
world,
ref
view,
ref
projection,
ref
worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
dirtyFlags = EffectHelpers.SetLightingMatrices(dirtyFlags,
ref
world,
ref
view, worldParam, worldInverseTransposeParam, eyePositionParam);
if
((dirtyFlags & EffectDirtyFlags.MaterialColor) != 0)
{
EffectHelpers.SetMaterialColor(
true
, alpha,
ref
diffuseColor,
ref
emissiveColor,
ref
ambientLightColor, diffuseColorParam, emissiveColorParam);
dirtyFlags &= ~EffectDirtyFlags.MaterialColor;
}
bool
newOneLight = !light1.Enabled && !light2.Enabled;
if
(oneLight != newOneLight)
{
oneLight = newOneLight;
dirtyFlags |= EffectDirtyFlags.ShaderIndex;
}
if
((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0)
{
int
shaderIndex = 0;
if
(!fogEnabled)
shaderIndex += 1;
if
(weightsPerVertex == 2)
shaderIndex += 2;
else
if
(weightsPerVertex == 4)
shaderIndex += 4;
if
(preferPerPixelLighting)
shaderIndex += 12;
else
if
(oneLight)
shaderIndex += 6;
shaderIndexParam.SetValue(shaderIndex);
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
}
}
#endregion
}
}