Showing posts with label Code Snippets. Show all posts
Showing posts with label Code Snippets. Show all posts

Friday, September 10, 2010

GETTING A REFERENCE TO A METHOD-CALLING OBJECT IN C#

Ok, after taking a month off from any blogging activity, I guess is time to catch up and blog again. And I believe there is no better way of doing so than to write an article explaining a way to get a reference to the object that calls a specific method.

For the last two years or so, I have been working hard on a new Content Manager replacement API for the XNA Framework to use on my own “never-ending” game engine (I will blog about this later on). In some early stages of development I found a way to solve this referencing burden that have been creating headaches to many devs, globally (being me, one of them, for a long time).

Although I must admit that at some point on the development cycle of my API I decided to abandon this workaround, after reading one of the articles mentioned by Shawn here (guess which one), I believe it’s a great opportunity to talk about it.

A little history … before founding this solution I tried everything: from attempting to use –with no luck- the info held in StackFrame instances and even delegates to finally “quit” and use a naive approach as passing the caller as a parameter in the signature of an operation. But, then came .NET Framework 3 and extension methods to the rescue!

Please bear with me that the example presented in this article is just a simple one to show how to use it, but believe my words when I say that it can be extended to complex scenarios.

Having said that … if you want to know which object is calling a certain method of a class like (which may or may not be sealed):

  public sealed class MyCalledClass
  {
    internal bool JustReturnTrue(int myParameter)
    {
      return true;
    }
  }

Being the caller, say, a specification of the following class:

  public abstract class MyCallingClass
  {
    public void Print()
    {
      Console.WriteLine("I am the object which is just about to call
         the monitored operation named 'JustReturnTrue' …"
);
    }
  }

Then all you have to do is to implement the following generic extension method for the types you want (in my example, “MyCallingClass”):

  public static class MyExtensionMethod
  {
    public static bool ExecuteOperation<T>
      (this T mycallingClass, int myParameter)
      where T: MyCallingClass
    {
      mycallingClass.Print(); // Replace it with your own stuff.
 
      return new MyCalledClass().JustReturnTrue(myParameter);
    }
  }

The trick here is to design and split assemblies and namespaces in a way that all instances of “MyCallingClass” cannot directly execute the “JustReturnTrue” operation (I leave that task as an exercise to the reader).

But there is one catch to watch closely, though. By doing this you are actually adding one more call (you know that), which is not generally a problem on Windows, but for the XBox 360 and all devices using the Compact Framework, it could turn out to be expensive if used lots of times on intensive or heavy tasks/loops.

But if you really need it when speed is not an issue or for operations where -for example- you need to set owners to something “automagically” behind the scenes and later assess whether an owner calls a restricted operation before executing it, then there you have it!

Just use it wisely …
~Pete

> Link to Spanish version.

Thursday, October 22, 2009

TWO NEW SAMPLES AT XNA CCO

You may be aware of this news, but just in case, I want to mention that two great new samples are now available on the site of XNA Creators Club:

Now, what’s great about them? Glad you’ve asked.

(I) Skinned Model Extension Sample

The first sample shows you how to extend the original Skinned Model sample so that:

  • You can “move” a part of a skinned model independently of an animation sequence (like in this case, the arm and or head),
  • You can position objects relative to a specific bone (in this case, a bat), and
  • You can properly attach bounding geometry to the animated model (in this case, spheres).

The code is a great source of technical knowledge regarding the skinning technique for programmers but, thanks to some minor glitches in the skinned model, they can also learn how cumbersome and picky are the processes of (not only modeling and unwrapping but) rigging, skinning and animating a 3D model, so that it looks ok in each and every (expected) situation before going gold.

First screenshot:

As you can see here, when you move the head one of the vertices remains (almost) still, which means that either it wasn’t attached to the neck's bone at all or, if it was, then its “weights” should be readjusted.

Second screenshot:

It seems that the part of the belt marked above in orange moves along with the hip whilst the rest of the belt moves with the torso. And so, the walking animation, even if it may behave as expected, looks kinda weird.

This is likely not relevant for programmers -being the programming part the only/main purpose of the sample itself, but for indies like me that tend to develop all parts of a game “in solo mode”, its a fabulous example of some of the headaches they are going to deal with on the artistic side of things.

(II) Primitives 3D Sample

The second one presents the set of classes you will need to create a cube, a sphere, a cylinder, a torus and the famous teapot!

These primitives are usually found in most famous modeling apps, among others, like 3D Studio Max or Maya.

You may wonder: why the teapot is considered a primitive? Well, thanks to the type of powerful operations used to create it, procedurally: bezier-curve calculation! Nice one, indeed …

One suggestion for the cylinder:

In case some of you want to create the triangles that conform each cap of the cylinder so that they lay out uniformly around a centered vertex, instead of towards one side -as shown above, then you’ll have to replace the following method in the “CylinderPrimitive” class:

/// <summary>
/// Helper method creates a triangle fan to close the ends of the cylinder.
/// </summary>
void CreateCap( int tessellation, float height, float radius, Vector3 normal )
{
  // Create cap indices.
  for ( int i = 0; i < tessellation - 2; i++ )
  {
    if ( normal.Y > 0 )
    {
      AddIndex( CurrentVertex );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
    }
    else
    {
      AddIndex( CurrentVertex );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
    }
  }
 
  // Create cap vertices.
  for ( int i = 0; i < tessellation; i++ )
  {
    Vector3 position = GetCircleVector( i, tessellation ) * radius +
                       normal * height;
 
    AddVertex( position, normal );
  }
}

So that, instead, it looks similar to:

/// <summary>
/// Helper method that creates a cap laying out triangles around a centered vertex.
/// </summary>
void CreateCap( int tessellation, float height, float radius, Vector3 normal )
{
  // Add a vertex in the center of the cap.
  AddVertex( normal * height, normal );
 
  // Create cap indices, taking into account the centered vertex (that is why
  // we use "CurrentVertex - 1" as the first element of the corresponding index).
  for ( int i = 0; i < tessellation; i++ )
  {
    if ( normal.Y > 0 )
    {
      AddIndex( CurrentVertex - 1 );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
    }
    else
    {
      AddIndex( CurrentVertex - 1 );
      AddIndex( CurrentVertex + ( i + 2 ) % tessellation );
      AddIndex( CurrentVertex + ( i + 1 ) % tessellation );
    }
  }
 
  // Create cap vertices.
  for ( int i = 0; i < tessellation; i++ )
  {
    Vector3 position = GetCircleVector( i, tessellation ) * radius +
                       normal * height;
 
    AddVertex( position, normal );
  }
}

Being the result:

This isn’t critical at all, but for the sake of (better) unwrapping -and even of creating more “sections” (rings) on each cap, programmatically- 2 centered vertices and a few more triangles come in handy without hurting performance.

Code on!
~Pete

 

> Link to Spanish version.

Sunday, July 05, 2009

“XNAVATARS” - PART 2 – NO SHADOWS?

As promised, I’m hereby posting the second part of the series about using Avatars with XNA GS on the XBox 360.

If you remember, on my first article I showed how to draw an animated avatar on screen taking into account transitions between two animations.

In this part, I will talk about one factor that will help you improve a little bit the eye-candy in your game when using avatars: shadows.

As you may know, the Avatar’s rendering system does not allow us to use a custom shader effect to render an avatar on screen; instead, we must only use the built-in system to accomplish that task.

On one side, this simplify things but on the other, it limits our possibilities a little bit.

Casting shadows is an example of this limitation. As I will show you in a minute or two, a “cheap” workaround can can used, for simple games.

The technique I’ll use is known as “Flat Shadows” (the XNA Framework includes all we need for it). It is a rather basic substitute for “real” shadows, but it will do the trick just fine for projects that don’t require “picky” shadow effects.

We will be using the project I had included last time as a starting point and mainly focus on the code to add and or modify.

1. Fields to add:

private Matrix[] transitionTransforms, shadowTransforms;
...
private Plane plane;
private float lightRotation;
private Model floor;

What’s new? The ‘shadow transforms’ array will store the matrices we need to flatten the model based on a reference plane, which we also define.

2. The constructor:

...
// Create the array of matrices that will hold bone transforms for shadows.
this.shadowTransforms = new Matrix[ 71 ];
...

Nothing fancy here. Just create the array that will hold the collection of matrices to flatten the model.

3. Initializing the game:

/// <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()
{
    this.plane = new Plane( Vector3.Up, 0 );
 
    // As usual, initialize all compoenents.
    base.Initialize();
}

We just create the reference plane with a normal facing up and without moving along that normal (so it’s a XZ plane where the Y coordinate is zero, initially).

4. Loading content:

...
// Set the "World" value wit a rotarion of 180º.
this.avatarRenderer.World = Matrix.CreateTranslation(
    Vector3.Right * -1 + Vector3.Up * 0 + Vector3.Forward * -1 ) *
    Matrix.CreateRotationY( MathHelper.Pi );
...

We just modify the line that places the avatar in the world.

5. Updating the game:

We will only add this line:

...
// Update the value used to rotate the light.
this.lightRotation += .5f * (float)gameTime.ElapsedGameTime.TotalSeconds;
...

So the light will rotate to show the effect.

6. Drawing the avatar:

/// <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 );
}

Here’s where most changes occur; the game ...:

  1. ... draws the avatar as on my previous example,
  2. ... changes the values of the lights that affect the avatar,
  3. ... adjusts the depth-bias to avoid any eventual z-fight,
  4. ... rotates the light and adjusts the plane altitude before flattening the model,
  5. ... updates the position of the flattened model using the static method of the matrix struct, which is named in the code as “CreateShadow”,
  6. ... draws the fake shadow, and finally ...
  7. ... restores the values of the lights and position of the avatar’s model meshes.

7: Changes to transitions code: none.

If everything goes fine you will see something like this:

Things to notice, though:

  1. Shadows will be drawn even when there’s no elements to cast shadows on (see how part of the shadow is rendered beyond the “floor” for a short period of time),
  2. You will have to modify the location of the shadow when the position of the avatar changes its height (i.e.: if jumping),
  3. The model’s meshes are flattened onto a reference plane, so it will only work for objects on that specific plane (like, in my example, a floor), and
  4. Thus, there’s no self-shadowing.

A more precise approach would be extending this example by trying to use the stencil buffer and depth data, as explained at the end of this thread.

Well, this is it for today. You can find the source code for this example here.

Cheers!
~Pete

> Link to Spanish version.