-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Skinny animation sample. * Add CustomPipelineManager to process animations content.
- Loading branch information
Showing
31 changed files
with
1,739 additions
and
277 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
TGC.MonoGame.Samples/Animations/DataTypes/AnimationBone.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace TGC.MonoGame.Samples.Animations.DataTypes; | ||
|
||
/// <summary> | ||
/// Keyframes are grouped per bone for an animation clip. | ||
/// </summary> | ||
public class AnimationBone | ||
{ | ||
/// <summary> | ||
/// The keyframes for this bone. | ||
/// </summary> | ||
public List<Keyframe> Keyframes { get; set; } = new(); | ||
|
||
/// <summary> | ||
/// The bone name for these keyframes. | ||
/// Each bone has a name so we can associate it with a runtime model. | ||
/// </summary> | ||
public string Name { get; set; } = string.Empty; | ||
} |
24 changes: 24 additions & 0 deletions
24
TGC.MonoGame.Samples/Animations/DataTypes/AnimationClip.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace TGC.MonoGame.Samples.Animations.DataTypes; | ||
|
||
/// <summary> | ||
/// An animation clip is a set of keyframes with associated bones. | ||
/// </summary> | ||
public class AnimationClip | ||
{ | ||
/// <summary> | ||
/// The bones for this animation clip with their keyframes. | ||
/// </summary> | ||
public List<AnimationBone> Bones { get; set; } = new(); | ||
|
||
/// <summary> | ||
/// Duration of the animation clip. | ||
/// </summary> | ||
public double Duration { get; set; } | ||
|
||
/// <summary> | ||
/// Name of the animation clip. | ||
/// </summary> | ||
public string Name { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using Microsoft.Xna.Framework; | ||
|
||
namespace TGC.MonoGame.Samples.Animations.DataTypes; | ||
|
||
/// <summary> | ||
/// An Keyframe is a rotation and Translation for a moment in time. | ||
/// It would be easy to extend this to include scaling as well. | ||
/// </summary> | ||
public class Keyframe | ||
{ | ||
/// <summary> | ||
/// The rotation for the bone. | ||
/// </summary> | ||
public Quaternion Rotation { get; set; } | ||
|
||
/// <summary> | ||
/// The keyframe time. | ||
/// </summary> | ||
public double Time { get; set; } | ||
|
||
/// <summary> | ||
/// The Translation for the bone. | ||
/// </summary> | ||
public Vector3 Translation { get; set; } | ||
|
||
public Matrix Transform | ||
{ | ||
get => Matrix.CreateFromQuaternion(Rotation) * Matrix.CreateTranslation(Translation); | ||
set | ||
{ | ||
var transform = value; | ||
transform.Right = Vector3.Normalize(transform.Right); | ||
transform.Up = Vector3.Normalize(transform.Up); | ||
transform.Backward = Vector3.Normalize(transform.Backward); | ||
Rotation = Quaternion.CreateFromRotationMatrix(transform); | ||
Translation = transform.Translation; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace TGC.MonoGame.Samples.Animations.DataTypes; | ||
|
||
/// <summary> | ||
/// Class that contains additional information attached to the model and shared with the runtime. | ||
/// </summary> | ||
public class ModelExtra | ||
{ | ||
/// <summary> | ||
/// Animation clips associated with this model. | ||
/// </summary> | ||
public List<AnimationClip> Clips { get; set; } = new(); | ||
|
||
/// <summary> | ||
/// The bone indices for the skeleton associated with any skinned model. | ||
/// </summary> | ||
public List<int> Skeleton { get; set; } = new(); | ||
} |
161 changes: 161 additions & 0 deletions
161
TGC.MonoGame.Samples/Animations/Models/AnimatedModel.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
using System.Collections.Generic; | ||
using Microsoft.Xna.Framework; | ||
using Microsoft.Xna.Framework.Content; | ||
using Microsoft.Xna.Framework.Graphics; | ||
using TGC.MonoGame.Samples.Animations.DataTypes; | ||
|
||
namespace TGC.MonoGame.Samples.Animations.Models; | ||
|
||
/// <summary> | ||
/// An enclosure for an XNA Model that we will use that includes support for Bones, animation, and some manipulations. | ||
/// </summary> | ||
public class AnimatedModel | ||
{ | ||
/// <summary> | ||
/// The Model asset name. | ||
/// </summary> | ||
private readonly string _assetName; | ||
|
||
/// <summary> | ||
/// The underlying Bones for the Model. | ||
/// </summary> | ||
private readonly List<Bone> _bones = new(); | ||
|
||
/// <summary> | ||
/// The actual underlying XNA Model. | ||
/// </summary> | ||
private Model _model; | ||
|
||
/// <summary> | ||
/// Extra data associated with the XNA Model. | ||
/// </summary> | ||
private ModelExtra _modelExtra; | ||
|
||
/// <summary> | ||
/// An associated animation clip Player. | ||
/// </summary> | ||
private AnimationPlayer _player; | ||
|
||
/// <summary> | ||
/// The Model animation clips. | ||
/// </summary> | ||
public List<AnimationClip> Clips => _modelExtra.Clips; | ||
|
||
/// <summary> | ||
/// Creates the Model from an XNA Model. | ||
/// </summary> | ||
/// <param name="assetName">The name of the asset for this Model.</param> | ||
public AnimatedModel(string assetName) | ||
{ | ||
_assetName = assetName; | ||
} | ||
|
||
/// <summary> | ||
/// Play an animation clip. | ||
/// </summary> | ||
/// <param name="clip">The clip to play.</param> | ||
/// <returns>The Player that will play this clip.</returns> | ||
public AnimationPlayer PlayClip(AnimationClip clip) | ||
{ | ||
// Create a clip Player and assign it to this Model. | ||
_player = new AnimationPlayer(clip, this); | ||
return _player; | ||
} | ||
|
||
/// <summary> | ||
/// Update animation for the Model. | ||
/// </summary> | ||
public void Update(GameTime gameTime) | ||
{ | ||
_player.Update(gameTime); | ||
} | ||
|
||
/// <summary> | ||
/// Draw the Model. | ||
/// </summary> | ||
/// <param name="world">A world matrix to place the Model.</param> | ||
/// <param name="view">The view matrix, normally from the camera.</param> | ||
/// <param name="projection">The projection matrix, normally from the application.</param> | ||
public void Draw(Matrix world, Matrix view, Matrix projection) | ||
{ | ||
// Compute all of the bone absolute transforms. | ||
var boneTransforms = new Matrix[_bones.Count]; | ||
|
||
for (var i = 0; i < _bones.Count; i++) | ||
{ | ||
var bone = _bones[i]; | ||
bone.ComputeAbsoluteTransform(); | ||
|
||
boneTransforms[i] = bone.AbsoluteTransform; | ||
} | ||
|
||
// Determine the skin transforms from the skeleton. | ||
var skeleton = new Matrix[_modelExtra.Skeleton.Count]; | ||
for (var s = 0; s < _modelExtra.Skeleton.Count; s++) | ||
{ | ||
var bone = _bones[_modelExtra.Skeleton[s]]; | ||
skeleton[s] = bone.SkinTransform * bone.AbsoluteTransform; | ||
} | ||
|
||
// Draw the Model. | ||
foreach (var modelMesh in _model.Meshes) | ||
{ | ||
foreach (var effect in modelMesh.Effects) | ||
{ | ||
var skinnedEffect = effect as SkinnedEffect; | ||
skinnedEffect.World = boneTransforms[modelMesh.ParentBone.Index] * world; | ||
skinnedEffect.View = view; | ||
skinnedEffect.Projection = projection; | ||
skinnedEffect.EnableDefaultLighting(); | ||
skinnedEffect.PreferPerPixelLighting = true; | ||
skinnedEffect.SetBoneTransforms(skeleton); | ||
} | ||
|
||
modelMesh.Draw(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Load the Model asset from content. | ||
/// </summary> | ||
public void LoadContent(ContentManager content) | ||
{ | ||
_model = content.Load<Model>(_assetName); | ||
_modelExtra = _model.Tag as ModelExtra; | ||
|
||
ObtainBones(); | ||
} | ||
|
||
/// <summary> | ||
/// Get the Bones from the Model and create a bone class object for each bone. We use our bone class to do the real | ||
/// animated bone work. | ||
/// </summary> | ||
private void ObtainBones() | ||
{ | ||
_bones.Clear(); | ||
foreach (var bone in _model.Bones) | ||
{ | ||
// Create the bone object and add to the hierarchy. | ||
var newBone = new Bone(bone.Name, bone.Transform, bone.Parent != null ? _bones[bone.Parent.Index] : null); | ||
|
||
// Add to the Bones for this Model. | ||
_bones.Add(newBone); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Find a bone in this Model by name. | ||
/// </summary> | ||
public Bone FindBone(string name) | ||
{ | ||
foreach (var bone in _bones) | ||
{ | ||
if (bone.Name == name) | ||
{ | ||
return bone; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
TGC.MonoGame.Samples/Animations/Models/AnimationPlayer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
using Microsoft.Xna.Framework; | ||
using TGC.MonoGame.Samples.Animations.DataTypes; | ||
|
||
namespace TGC.MonoGame.Samples.Animations.Models; | ||
|
||
/// <summary> | ||
/// Animation Clip player. | ||
/// It maps an animation Clip onto a Model. | ||
/// </summary> | ||
public class AnimationPlayer | ||
{ | ||
/// <summary> | ||
/// We maintain a BoneInfo class for each bone. | ||
/// This class does most of the work in playing the animation. | ||
/// </summary> | ||
private readonly BoneInfo[] _boneInfo; | ||
|
||
/// <summary> | ||
/// The Clip we are playing. | ||
/// </summary> | ||
private readonly AnimationClip _clip; | ||
|
||
/// <summary> | ||
/// Current position in time in the clip. | ||
/// </summary> | ||
private float _position; | ||
|
||
/// <summary> | ||
/// Constructor for the animation player. | ||
/// It makes the association between a Clip and a Model and sets up for playing. | ||
/// </summary> | ||
public AnimationPlayer(AnimationClip clip, AnimatedModel model) | ||
{ | ||
_clip = clip; | ||
|
||
// Create the bone information classes. | ||
var boneCount = clip.Bones.Count; | ||
_boneInfo = new BoneInfo[boneCount]; | ||
|
||
for (var b = 0; b < _boneInfo.Length; b++) | ||
{ | ||
// Create it. | ||
_boneInfo[b] = new BoneInfo(clip.Bones[b]); | ||
|
||
// Assign it to a Model bone. | ||
_boneInfo[b].SetModel(model); | ||
} | ||
|
||
Rewind(); | ||
} | ||
|
||
/// <summary> | ||
/// The Looping option. | ||
/// Set to true if you want the animation to loop back at the end. | ||
/// </summary> | ||
public bool Looping { get; set; } | ||
|
||
/// <summary> | ||
/// Current Position in time in the Clip. | ||
/// </summary> | ||
private float Position | ||
{ | ||
get => _position; | ||
set | ||
{ | ||
if (value > Duration) | ||
{ | ||
value = Duration; | ||
} | ||
|
||
_position = value; | ||
foreach (var bone in _boneInfo) | ||
{ | ||
bone.SetPosition(_position); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// The Clip duration. | ||
/// </summary> | ||
public float Duration => (float)_clip.Duration; | ||
|
||
/// <summary> | ||
/// Reset back to time zero. | ||
/// </summary> | ||
public void Rewind() | ||
{ | ||
Position = 0; | ||
} | ||
|
||
/// <summary> | ||
/// Update the Clip Position. | ||
/// </summary> | ||
public void Update(GameTime gameTime) | ||
{ | ||
Position += (float)gameTime.ElapsedGameTime.TotalSeconds; | ||
if (Looping && Position >= Duration) | ||
{ | ||
Position = 0; | ||
} | ||
} | ||
} |
Oops, something went wrong.