Sunday, June 6, 2010

XSI: Animate different instances at the same time

There is a bug/flaw when trying to animate individually different instances of the same model. Due to the model and animations are loaded only once (even if there are multiple models), models are shared.

One possible solution is to have one different file for each instance of the model. This is a bad design solution, due to we are replicating the same file but with a different name.

The second and best solution is to solve the actual flaw. The main problem is that every instance of the same model keeps track of the currentTime, instead of one per instance. The way to get around this is to:

1. Keep a local TimeSpan per instance of the model

2. Create an extension method of PlayBack().


public static void PlayBackAt( this XSIAnimationContent Animations, TimeSpan time, float blend )
{
if( (Animations.Loop) &&
(Animations.Duration > TimeSpan.Zero) )
{
long ticks = time.Ticks % Animations.Duration.Ticks;
time = TimeSpan.FromTicks(ticks);
}
Animations.CurrentTime = time;
foreach( KeyValuePair<string, XSIAnimationChannel> channel in Animations.Channels )
{
channel.Value.PlayBack(Animations.CurrentTime, blend);
}
}


This method should be called instead of the original PlayBack(). The extension method uses the old one but taking into account the local currentTime of each instance.

3. Call the PlayBackAt routine before the instance is drawn.

//------------------------------------------------------------
public void DrawModel( Game game, Camera camera )
{
Render();
this.model.DrawModel( game, camera, this );
}

//------------------------------------------------------------
private void Render()
{
TimeSpan elapsedTime = TimeSpan.FromTicks(
(long (gameTime.ElapsedGameTime.Ticks * this.thumbstick) );

if( this.model.Animations.Count > 0)
{
if( this.currentBlendTime < this.blendTime )
{
this.model.Animations[this.model.OldAnimationIndex].PlayBackAt( TimeSpan.Parse("0"), 1.0f );
this.currentBlendTime += (float)elapsedTime.TotalSeconds;
}
else
{
this.model.OldAnimationIndex = this.model.AnimationIndex;
this.currentBlendTime = this.blendTime;
}
if( this.model.AnimationIndex < this.model.Animations.Count )
{
if ( this.blendTime != 0.0 )
{
Blend =
this.currentBlendTime / this.blendTime;
}
this.model.Animations[currentAnimation].PlayBackAt( animationTimeElapsed, Blend );
}
}
}