Hello.
Lately I’ve been experimenting with 3D and trying to load animated models into the game. After some research I’ve found out how it’s supposed to be done, so I’ve attempted to do it myself following SkinnedModel tutorial , buuut… I’ve come across few obstacles I don’t know how to deal with.
There are actually 2 problems with might be caused by the same issue:
- On one model, I’m getting
Value does not fall within the expected range
exception when callingSkinnedEffect.SetBoneTransforms(Matrix[])
method. Matrix array containing animation transformations is properly populated, so I really have no idea what the problem is. - On few other models, I’m not getting anything rendered on screen at all. However, if I comment the line containing
SetBoneTransforms()
call, I do get something on screen… but (most probably) way oversized.
Example (I think lack of texture is unrelated though):
The sample code using Aether.Extras (same thing happens with BetterSkinnedModel as well):
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using tainicom.Aether.Animation;
namespace _3DFightTest
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Matrix _world;
Matrix _view;
Matrix _projection;
Model _knight;
Animations _animations;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
_world = Matrix.CreateTranslation(Vector3.Zero);
_view = Matrix.CreateLookAt(new Vector3(0, 500, 500), Vector3.Zero, Vector3.Up);
_projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 1000f);
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
_knight = Content.Load<Model>("Animated Human");
_animations = _knight.GetAnimations();
_animations?.SetClip(_animations.Clips["Human Armature|Idle"]);
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
// TODO: Add your update logic here
_animations?.Update(gameTime.ElapsedGameTime, true, Matrix.Identity);
base.Update(gameTime);
}
/// <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)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
foreach (var mesh in _knight.Meshes)
{
foreach (var part in mesh.MeshParts)
{
var effect = part.Effect as SkinnedEffect;
effect.SetBoneTransforms(_animations.AnimationTransforms);
effect.World = _world;
effect.View = _view;
effect.Projection = _projection;
effect.EnableDefaultLighting();
}
mesh.Draw();
}
base.Draw(gameTime);
}
}
}
Now, I’ve tried multiple things to resolve issues, and only managed to resolve when model has more bones that the max limit allows for. Other issues have already started to frustrate me greatly.
What I’ve already tried:
- Using both SkinnedModel and BetterSkinnedModel to extend Content Pipeline (with appropriate loading and drawing code from my side)
- Using @nkast’s Aether.Extras extension for Monogame (preferred due to how code is used and the fact that it’s possible to define max amount of bones in Content Pipeline tool)
- Testing with different models
What I did not yet try:
- Using Bos FBX Importer/Exporter as suggested by @KonajuGames
- Making my own animated model in Blender
Now, from what I’ve understood, at this moment it’s not strictly problem with the code (either Monogame’s default or extensions), but with the model itself. I’ve also read that models should be in T-position when loading, and I’ve changed one model accordingly, but it didn’t help. Since I’ve pretty much exhausted possible solutions (as mentioned Bos FBX exporter) and I’d really like things to be as much out-of-the-box as possible (easier to restore things), I’m turning here for some help and suggestions. I just hope problem isn’t related to Blender… and that I won’t need to switch to Unity X)
Thanks in advance!
EDIT: I’ve also tried using Blender importer extension for Content Pipeline. While it did fix the “Value not in expected range” issue with one model, said model is still deformed. With other model I managed somehow to get part of it on screen, but… yeah, it’s unrecognizable too and I have no idea what the issue is.