Texture Modification using Render Targets, with some Stencil Buffer Action

August 10, 2010 by Dave · 6 Comments
Filed under: C#, Game Programming, WP7DEV, XNA, XNA 4.0 

Sometimes you need to modify a texture while your game is running, and there are a number of ways to do this. One of the first things newer game programmers often try to do is use Texture2D.GetData to copy the texture data from the GPU to an array on the CPU, modify the bytes, and then send it back to the GPU with Texture2D.SetData.

This is a bad idea on many levels. Beyond issues with pipeline stalls, GetData and SetData can be slow, especially when working with a large texture. Any time you’re tempted grab data from the GPU for use on the CPU you should very carefully consider all of your options. There are often other solutions that let you keep the data entirely on the GPU and accomplish the same thing.

This tutorial will use an example that could be solved with GetData and SetData, and show you another alternative using render targets and the stencil buffer that will let you perform the same function entirely on the GPU.

CPU Craters

Let’s pretend you want to draw 2D planet, and periodically add a crater to it.  You want a hole to appear somewhere on the planet, so it looks like part of it was removed.

You could do this using the GetData/SetData method by getting the data from a texture into an array, setting the color to the background (or alpha to 0) in the shape of the crater, then writing the data back to the texture. Or you could be a little cleverer and eliminate GetData by always keeping the data in the array, but you still have to do the SetData to get it into the texture on the GPU each time it’s changed.

GPU Craters

The method we’ll use to do this entirely on the GPU involves several steps.  First, we need a couple of resources.  We’ll use a simple textured circle for a planet, and a “crater” shaped texture for the crater.

It’s important to note that the black areas on these have an alpha value of 0, meaning completely transparent. For the planet this just lets us draw the round shape over the background without looking like a square image. But for the crater image the alpha value is very important since it will control what part of the crater image is removed from the planet.

Next, we need to set up two render targets (these will be referred to later as Render Target A, and Render Target B). When we need to add a crater, one of these will be used as a target for drawing to, while the other used as a texture. The next time we add a crater they will swap roles – the texture will become the target, and the target will become the texture. This is called “ping-ponging” and will be discussed more fully later.

Once we have these resources ready to go, the method for adding a crater goes like this:

  1. Activate Render Target A using GraphicsDevice.SetRenderTarget.
  2. Clear the graphics device, setting the color to solid black, and the stencil buffer to 0.
  3. Set up the stencil buffer state so whatever we draw writes a value of 1 to the stencil buffer.
  4. Set up the alpha test state so we only draw where the alpha value is zero.
  5. Draw the crater texture. Because of the way we’ve set up the graphics device, only the parts of the crater texture that have alpha = 0 will be drawn, and those parts will write a 1 to the stencil buffer.  So what we have at this point is a “mask” in the stencil buffer that we can use in the next step. The white area in the following image represents the stencil mask we’ve set up – the stencil buffer contains “1” in the white area, and “0” everywhere else.
  6. Set up the stencil buffer so when we draw, anything that has a value of 1 in the stencil buffer will be masked out – meaning it won’t draw.
  7. Draw the “planet texture”. Because of the way we’ve set up the graphics device, anything with a 1 in the stencil buffer won’t be drawn – since these 1’s are in the shape of a crater, that shape will be masked out of the planet texture, leaving holes that look like craters.
  8. Set the render target to the backbuffer.  We can now access Render Target A as a texture, and that texture contains the planet texture with a crater-shaped hole in it.

Step 5

Step 7

From now on, until we need to add another crater, we can treat Render Target A as a texture and draw it using SpriteBatch, and we’ll have a nice crater. Now, what if we need to add another crater? This is where the ping-ponging comes in. Since Render Target A is now the “planet texture”, we need to be able to draw somewhere else when we’re filling in the stencil buffer with our crater shape. It just so happens that we set up another place to draw to, Render Target B. 

So now, in Step 1, instead of activating Render Target A we need to activate Render Target B and draw the crater shapes into that.  But what happens when we get to Step 7? Well, the “planet texture” is now in Render Target A, so we draw that.  And in Step 8, Render Target B now contains our new planet texture with two craters.

And if we add a third crater then we’re back to where we started – drawing to Render Target A, and using Render Target B as the source texture. In other words, we “ping-pong” between the two render targets – each time we need to modify the texture, one is used for a texture, and one is used for drawing to, and then those roles are swapped.

You may have noticed that there’s one issue here.  The first time through, Render Target B has nothing in it, so we can’t use it as the planet texture.  This can be handled by using the actual planet texture the first time, and the render target thereafter.

The Code

Now let’s walk through the code involved, using XNA 4.0.  You can do this in 3.1, but you’ll have to make significant changes when creating the render targets and setting the render states.

The complete code is in the downloadable project linked at the end of the tutorial.  We’ll just go through the highlights here, referring to the steps mentioned above as we go.

The XNA 4.0 API has been changed substantially where render states are concerned, and for the better. Render states have been grouped by functionality into several classes. You create instances of these classes to represent the state you want, then set them on the graphics device, or pass them to SpriteBatch.  So first we need to create these render state objects.

Set Up Render State Objects

For Step 3, we need to use the DepthStencilState class to set up the device to always set the stencil buffer to 1. We enable the stencil buffer, set the stencil function to Always, the pass operation to Replace, and ReferenceStencil to 1. This means that as we’re drawing, each pixel will Always pass, and the value in the stencil buffer will be Replaced with 1.

      stencilAlways = new DepthStencilState();
      stencilAlways.StencilEnable = true;
      stencilAlways.StencilFunction = CompareFunction.Always;
      stencilAlways.StencilPass = StencilOperation.Replace;
      stencilAlways.ReferenceStencil = 1;
      stencilAlways.DepthBufferEnable = false;
 

And for Step 4 we need to use the standard AlphaTestEffect so we can draw the asteroid texture only where the alpha value is 0.

 

      Matrix projection = Matrix.CreateOrthographicOffCenter(0, PlanetDataSize, PlanetDataSize, 0, 0, 1);
      Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
      alphaTestEffect = new AlphaTestEffect(GraphicsDevice);
      alphaTestEffect.VertexColorEnabled = true;
      alphaTestEffect.DiffuseColor = Color.White.ToVector3();
      alphaTestEffect.AlphaFunction = CompareFunction.Equal;
      alphaTestEffect.ReferenceAlpha = 0;
      alphaTestEffect.World = Matrix.Identity;
      alphaTestEffect.View = Matrix.Identity;
      alphaTestEffect.Projection = halfPixelOffset * projection;
 

We first set up an orthographic projection matrix that matches SpriteBatch. We set AlphaFunction to Equal, and ReferenceAlpha to 0.  This means the alpha test will pass whenever the alpha value we’re drawing is equal to 0. In our crater texture, the crater area has an alpha value of 0, while the surrounding area has 1, so only the crater area will be drawn.

For Step 6 we need a stencil buffer state that allows drawing only where the stencil buffer contains a 0. We enable the stencil buffer, set the stencil function to Equal, the pass operation to Keep, and the reference stencil to 0. This means that when we’re drawing, each pixel will pass if the value in the stencil buffer is Equal to 0.

      stencilKeepIfZero = new DepthStencilState();
      stencilKeepIfZero.StencilEnable = true;
      stencilKeepIfZero.StencilFunction = CompareFunction.Equal;
      stencilKeepIfZero.StencilPass = StencilOperation.Keep;
      stencilKeepIfZero.ReferenceStencil = 0;
      stencilKeepIfZero.DepthBufferEnable = false;
 

Create Render Targets

Now that we have the render state objects created, it’s time to create the render targets.  Both are the same, so just one is shown here. This creates a render target with a Color format, and a depth format that includes a stencil buffer.

      renderTargetA = new RenderTarget2D(GraphicsDevice, PlanetDataSize, PlanetDataSize,
                          false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8,
                          0, RenderTargetUsage.DiscardContents);

Draw the Crater Mask

Next up is drawing the crater masks (Steps 2-5). First we activate the render target, clear it to solid black, and clear the stencil buffer to 0.

 

      GraphicsDevice.SetRenderTarget(activeRenderTarget);
      GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.Stencil,
                           new Color(0, 0, 0, 1), 0, 0);
 

Next we begin a SpriteBatch, passing in the stencilAlways and alphaTestEffect objects that we created earlier. Calculate some random rotation, size the crater texture using a Rectangle, and call SpriteBatch.Draw to draw the crater.

      spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque,
                        null, stencilAlways, null, alphaTestEffect);
      Vector2 origin = new Vector2(craterTexture.Width * 0.5f,
                                   craterTexture.Height * 0.5f);
      float rotation = (float)random.NextDouble() * MathHelper.TwoPi;
      Rectangle r = new Rectangle((int)position.X, (int)position.Y, 50, 50);

      spriteBatch.Draw(craterTexture, r, null, Color.White, rotation,
                       origin, SpriteEffects.None, 0);

      spriteBatch.End();

 

Draw the Planet Texture

Now we need to draw the latest planet texture, using the stencil buffer to mask out the craters (Steps 6-7). We begin a SpriteBatch, passing in the stencilKeepIfZero object we created earlier. Note that the first time we draw the actual planet texture, but subsequently we draw using the texture from the previous iteration.

      spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque,
                        null, stencilKeepIfZero, null, null);

      if (firstTime)
      {
        spriteBatch.Draw(planetTexture, Vector2.Zero, Color.White);
        firstTime = false;
      }
      else
        spriteBatch.Draw(textureRenderTarget, Vector2.Zero, Color.White);

      spriteBatch.End();

 

Swap Render Targets

Finally we activate the backbuffer render target.

      GraphicsDevice.SetRenderTarget(null);
 

And then swap the render targets as discussed previously.

      RenderTarget2D t = activeRenderTarget;
      activeRenderTarget = textureRenderTarget;
      textureRenderTarget = t;
 

 In the main Draw function, you draw the latest cratered planet using the textureRenderTarget. Of course, you need to deal with using the planet texture the first time through though. The downloadable code shows one simple way to do that.

      GraphicsDevice.Clear(Color.CornflowerBlue);
      spriteBatch.Begin();
      spriteBatch.Draw(textureRenderTarget, planetPosition, Color.White);
      spriteBatch.End();
 

Conclusion

And there you have it, a powerful technique for altering textures during your game. Doing this entirely on the GPU is quite a bit more complex than GetData/SetData, but is well worth the extra trouble.

There are some things you can do to improve this technique. If you need to add a lot of craters, rather than adding them one at a time you can batch them up for a while, then in Step 5 draw all of them at once.

I hope you found this tutorial informative. Learning about render targets and stencil buffers opens up a whole new world of possibilities beyond just making craters. What other uses can you think of?

Download the sample XNA 4.0 project

Download the sample XNA 3.1 project

GPU Geometry Map Rendering – Part 1

January 9, 2010 by Dave · 1 Comment
Filed under: C#, Procedural Planet 

I spent the past week moving my procedural planet renderer’s geometry map creation code from the CPU to the GPU. It didn’t go as smoothly as I would have liked, but in a way that was a good thing since I gained a much deeper understanding of some render pipeline things that I had been taking for granted. I also learned how to use PIX for shader debugging, which I now realize I should’ve done a long time ago. I hope to walk through some shader debugging in a later post.
 

CPU Geometry Maps

You may recall that a planet is defined as a cube with six faces. Each face can be thought of as a single flat plane when it comes to generating height values, and going forward we’ll do just that by considering  only the front cube face.

The cube face is defined with a coordinate system with (-1, -1) on the lower left, and (1, 1) at the upper right. This is very similar to clip-space, but we’ll refer to it as cube-face-space or just face-space.

 

 Cube-Face-Space
 

The geometry map can be thought of as a grid overlaying the cube face. Each position in the grid can also be thought of as a pixel in a heightmap. The grid can be as detailed as needed, but each dimension should match 2^n+1 – for example: 33×33, 65×65 – in order to help with subdividing. A very commonly  used size is 33×33, and this is what I use. However, for this discussion we’ll use 5×5 to make things a bit easier to talk about. The geometry map has its own coordinate space, with (0, 0) on the lower left and (1, 1) on the upper right. Let’s call this geomap-space.

 

Geomap-Space
 

We need to generate a height value for each position in the geometry map grid. To do this we map from a geomap-space coordinate to a face-space coordinate, and use the result to generate the height at that position and store that height in the geometry map.

Here is a stripped down version of the CPU code I was using to create my geometry maps. The left, top, width, and height parameters define the face-space area we’re generating height values for.   For a 5×5 geometry map over the entire face this function would be called like this:
 

CreateGeometryMap(-1, 1, 2, 2, 5, 5);

 

public void CreateGeometryMap(float left, float top, float width, float height)
{
  // Calculate how far we need to move horizontally for each vertex
  // so the first is at "left", and the last is at "left + width".
  // Do the same for the vertical dimension. GeometryMapWidth and
  // GeometryMapHeight define the geometry map dimensions
  float horizontalStep = width / (GeometryMapWidth - 1);
  float verticalStep = height / (GeometryMapHeight - 1);

  float y = top - height;      // start at the bottom

  for (int gy = 0; gy < GeometryMapHeight; gy++)
  {
    float x = left;

    for (int gx = 0; gx < GeometryMapWidth; gx++)
    {
      geometrymap[gx * GeometryMapWidth + gy] = GetHeightAt(x, y);
      x += horizontalStep;
    }

    y += verticalStep;
  }
}

 
Let’s walk through what it’s doing, in just the horizontal dimension since the vertical is exactly the same. This is a key to understanding how the GPU version works, so don’t skip over it.

The code iterates over the geometry map pixels from 0 to 4. The first pixel corresponds to -1 in face-space, the last pixel corresponds to +1 in face-space.  The intervening pixels are found using the horizontalStep variable, which is calculated by dividing the face-space width by the geometry map width minus one. Remember that we passed in -1 for left, and 2 for width, so horizontalStep is 2 / (5 – 1), or 0.5.

The variable x starts out with the value left, or -1.  Walking through the inner loop we get these values for x:

gx = 0, x = -1
gx = 1, x = -0.5
gx = 2, x = 0
gx = 3, x = 0.5
gx = 4, x = 1.0

 

If our geometry map width was 33, horizontalStep would be 2 / (33 – 1) or 0.0625. The first few steps of the loop would look like this:

gx = 0, x = -1
gx = 1, x = -0.9375
gx = 2, x = -0.875
gx = 3, x = -0.8125
gx = 4, x = -0.75
gx = 5, x = -0.6875
gx = 6, x = -0.625

 

And so on out to gx = 32 and x = 1. As mentioned before, determining y works exactly the same.

 So, that pretty much takes care of the CPU method. It works great, it’s fairly simple, and it’s slower than a turtle crossing an Iowa road in winter. Well, to be fair, the GetHeightAt() function is slow because of the nature of the noise functions. Moving that functionality to the GPU is where we see the huge performance wins. So lets get to it.

GPU Geometry Maps

On the GPU side, we need to be able to generate exactly the same face-space coordinates as CalculateGeometryMap. Why does it have to be exact? Since the GPU only returns the height values, the CPU still has to determine the x and y values since they’re also used to create the actual vertices. If the x and y used by the GPU is different from the one used by CPU, bad things happen, like terrain patches shifting a pixel or two when they’re subdivided, and three 18 hour days spent trying to figure out why (yes, this is how I spent my week-long vacation from my “real” job).

When you think about needing the GPU to iterate over something in a general purpose way, what should immediately come to mind is texture coordinates.  I’m enough of a n00b at this stuff that it didn’t immediately come to my mind, so while letting Google do my thinking for me I ran across the Britonia blog, which looks like it will prove to be a very helpful resource when it comes to this whole planet creation business.

The idea I came across on that blog is to have the GPU interpolate texture coordinates in such as way as they match the x and y values on the CPU version. It was a breakthrough moment for me, I ran with it, and soon came to a screeching halt. Let’s start with the “running with it” part though.

First thing is to set up a render target which will hold our geometry map. The render target needs to match the size of our desired geometry map, so we just create it with the same dimensions. (Note that all of this code will be available in the sample linked at the end of part 2 of the article).

 

const int Width = 5;
const int Height = 5;

renderTarget = new RenderTarget2D(GraphicsDevice, Width, Height, 1,
                                  SurfaceFormat.Color, MultiSampleType.None,
                                  0, RenderTargetUsage.DiscardContents);

 
Note that I’m using SurfaceFormat.Color here. In the actual version you want SurfaceFormat.Single so you get 32-bit floating point goodness. In this case Color works out fine since we’re going to be examining the output in PIX and don’t really care what the final format looks like.

Next thing is to set up a full screen quad with pre-transformed vertices. Pre-transformed means we’re defining the vertices in clip space, so no transformation is necessary in the vertex shader. We can just pass the coordinates directly to the vertex shader with no changes. Also, we define texture coordinates that cover the full quad, so the GPU will interpolate them from 0 to 1 for us as it processes each pixel.

 

vertices = new VertexPositionTexture[4];
vertices[0] = new VertexPositionTexture(new Vector3(-1, 1, 0f), new Vector2(0, 1));
vertices[1] = new VertexPositionTexture(new Vector3(1, 1, 0f), new Vector2(1, 1));
vertices[2] = new VertexPositionTexture(new Vector3(-1, -1, 0f), new Vector2(0, 0));
vertices[3] = new VertexPositionTexture(new Vector3(1, -1, 0f), new Vector2(1, 0));

 
Note that “full screen” really means “full render target”. Because the render target is 5×5, and we’re rendering a full screen quad to it, the quad will contain 5×5 pixels, and our pixel shader will be executed for each of those pixels, with the texture coordinates interpolated over the pixels from 0 to 1 in each dimension.  (Yes, the screeching halt will soon be upon us).

The last thing we need is to tell the pixel shader what face-space dimensions to work with. These are the same parameters passed to the CreateGeometryMap function on the CPU.

 

quadEffect.Parameters["Left"].SetValue(-1.0f);
quadEffect.Parameters["Top"].SetValue(1.0f);
quadEffect.Parameters["Width"].SetValue(2.0f);
quadEffect.Parameters["Height"].SetValue(2.0f);

 

To rehash a bit, CreateGeometryMap required the face-space dimensions (left, top, width, height), as well as constant values defining the geometry map dimensions. For our pixel shader, the face-space dimensions are taken care of by the effect parameters, and the geometry map dimensions are taken care of by the render target dimensions.

All that’s left now is drawing the quad.

 

GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, vertices, 0, 2);

 

And, now the screeching halt…

The results were close, but not what I expected. Neighboring geometry maps were off by a pixel or two, and when quad tree nodes split, all the geometry seemed to shift a pixel or two. It was all very distracting and ugly. After spending quite awhile walking through code and trying to figure out where I went wrong, I finally decided it was time to install PIX.

And the rest will have to wait until the next post, where I’ll go through the shader itself, and walk through debugging it in PIX to see what’s going on.

Procedural Planet Engine Status

December 30, 2009 by Dave · 2 Comments
Filed under: C#, Procedural Planet, XNA 

Previously I mentioned I was going to do a mulligan on my procedural planet engine. The few hours I’ve worked on it so far have lead to a beautiful new architecture that’s doing most of the same things as before, as well as some major new things, using about 25% of the code.

Here is where  things stand currently. I’ll go through some of these in more detail in a later post:

The planet consists of a cube, with the vertices mapped to a sphere. Each of the six cube faces is a quad tree which is used for subdividing the terrain as you move closer to the planet. Each node in the quad tree represents a patch of terrain with 33×33 vertices that are spread out evenly to cover the patch’s area.

In the previous version the quad tree nodes were subdivided synchronously, which resulted in jerkiness when moving slowly, and outright 5 second waits when moving quickly if a lot of nodes needed to be subdivided. That was good enough then since my priorities were elsewhere, but it’s not good enough for the new version. Now, when a node needs to be split the request is queued on a separate thread. The current node will continue to draw until the split is complete. The split requests can be cancelled as well if the camera has moved elsewhere before the split request reaches the head of the queue.

The nice thing about this design is that if you’re moving very fast you end up getting fewer node splits because they’re cancelled before they happen since they’re no longer necessary. Conversely, if you’re moving slowly the splits can easily keep up with your location so you get all of the required detail.  On the con side, if you’re moving quickly down to a low level, then stop, it can take a bit for the queue to catch up generating the terrain patches, so the detail can take awhile to show up.

Generating a patch currently happens on the CPU using Perlin as a noise basis and various fractal algorithms such as fBm, Turbulence, and Ridged Multifractal. I will be moving this to the GPU over the coming weeks which will vastly improve the “catching up” problem mentioned previously. This will also enable creating procedural normal maps and textures on the fly.

So, the current version of the app lets me start out in space and fly to an Earth-sized planet down to ground level with ever increasing detail, and absolutely no stalling. The entire planet can be explored, but there is no texturing yet, and lighting is using vertex normals so it’s fairly ugly, but it gets the job done at this stage.

I think the next thing I will do is work on moving the patch generation to the GPU. This seemed like a daunting task 8  months ago, but it should be pretty straightforward now. This is a requirement to allow generating higher resolution procedural normal maps, which will be a big step in improving the look of the terrain.

So, that’s it for now.  In future posts I’ll go through some of these features in more detail and discuss how I did things.

Sprite Sheet Creator

August 25, 2009 by Dave · 1 Comment
Filed under: C#, Game Programming, Tools, XNA 

When developing the iPhone version of Guardian I manually created my sprite sheets.  I used individual sprites up until the end so everything was pretty much set in stone by the time I created the the sprite sheet.  Even then I ended up having to recreate the sprite sheet two or three times, and let me tell you, manually figuring out the texture coordinates isn’t a particularly pleasant experience. In this case I believe I made the right choice.  There were few enough sprites that I would have spent more time creating the tool than I would have saved.

The XBox version has quite a few more sprites, so I decided that spending time creating a sprite sheet tool was going to be well worth the effort.  It didn’t take too long to get it working well enough to use, and not too much longer than that to make it solid enough for distribution.

Sprite Sheet Creator

The application is released as open source under the MIT License.

Download SpriteSheetCreator.zip

Simplified XNA Message Boxes

June 11, 2009 by Dave · 2 Comments
Filed under: C#, Game Programming, XNA 

Shawn Hargreaves brings up the subject of how annoying async coding can be.  Calling a “begin” method, dealing with the completion callback function, handling the results – it’s all very ugly to keep track of, and often leads to very ugly code.

He wants to be able to write code like this (and so do I)…

 

 int? button = Guide.ShowMessageBox("Save Game",
                                       "Do you want to save your progress?",
                                       new string[] { "OK", "Cancel" },
                                       0, MessageBoxIcon.None);

    if (button == 0)
    {
        StorageDevice storageDevice = Guide.ShowStorageDeviceSelector();

        if (storageDevice != null)
        {
            using (StorageContainer storageContainer = storageDevice.OpenContainer("foo"))
            {
                ...
            }
        }
    }

 

 

It turns out that making async code work almost like this isn’t too bad to do. It basically involves creating a static class to encapsulate all of the various things you need to keep track of. Here is the fairly well commented code for the static class.

class SimpleMessageBox
  {
    private static int? dialogResult = null;
    public static bool Showing { get; set; }

    public static int? ShowMessageBox(string title, string text, IEnumerable  buttons, int focusButton, MessageBoxIcon icon)
    {
      // don't do anything if the guide is visible - one issue this handles is showing dialogs in quick
      // succession, we have to wait for the guide to go away before the next dialog can display
      if (Guide.IsVisible) return null;

      // if we have a result then we're all done and we want to return it
      if (dialogResult != null)
      {
        // preserve the result
        int? saveResult = dialogResult;

        // reset everything for the next message box
        dialogResult = null;
        Showing = false;

        // return the result
        return saveResult;
      }

      // return nothing if the message box is still being displayed
      if (Showing) return null;

      // otherwise show it
      Showing = true;
      Guide.BeginShowMessageBox(title, text, buttons, focusButton, icon, MessageBoxEnd, null);
      return null;
    }

    private static void MessageBoxEnd(IAsyncResult result)
    {
      dialogResult = Guide.EndShowMessageBox(result);

      // if no button was pressed then we want the result to be -1
      if (dialogResult == null)
        dialogResult = -1;
    }

 

Using the class involves calling SimpleMessageBox.ShowMessage(…) in your Update() method. You continue to call it each frame until it returns a result. This does require some game state information (i.e. your game state is SaveGameState or something similar) so it takes a little extra work, but you have to keep track of those sorts of states anyway.

Here’s a sample of the usage:

protected override void Update(GameTime gameTime)
    {
      base.Update(gameTime);

      if (saveGame)
      {
        // show the message box - we end up calling this each frame as long as we're in the saveGame state - it will
        // return null until the user presses a button or closes the guide - it returns -1 if the guide
        // is closed, otherwise it returns the button number
        int? button = SimpleMessageBox.ShowMessageBox("Save Game", "Do you want to save your progress?",
                                                      new string[] { "OK", "Cancel", "Repeat" }, 0, MessageBoxIcon.None);

        switch (button)
        {
          case -1:
            message = "No Button";
            saveGame = false;
            break;

          case 0:
            message = "Saved";
            saveGame = false;
            break;

          case 1:
            message = "Cancelled";
            saveGame = false;
            break;

          case 2:
            message = "Repeat";
            break;
        }
      }
    }

 

I haven’t use this code in a real project yet (just the sample), but it seems like it would work in quite a few situations. It’s a bit different than doing a message box in Windows since you have to realize you’re calling the ShowMessageBox method each frame. That aside, you can almost imagine that you’re using a blocking message box function.

Download Sample Project

Lens Flare Occlusion Using Texture Masking and XNA

May 30, 2009 by Dave · Leave a Comment
Filed under: C#, Game Programming, XNA 

Edit: Since Ziggyware is no more, this article now appears at Sgt. Conker.

I have an article posted on Ziggyware that discusses an alternate method to hardware occlusion queries for checking sun visibility to control lens flare intensity.  The article is part of a contest so I can’t post it here until the contest has been over for awhile.  I can link to it however.

Lens Flare Occlusion Using Texture Masking and XNA

Hope everyone enjoys it.

« Previous PageNext Page »