Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

The Shattering Screen Saver

  A screen saver that will make the screen break apart and fall to the bottom of the screen.
Zman's Diary

Difficulty: Intermediate
Time Required: 3-6 hours
Cost: Free
Software: Visual Basic or Visual C# Express Editions, February 2006 DirectX SDK
Hardware:
Download:

Last time not only was it the holidays, but I left you with a question about why the screen saver slowed down so much when you increased the tree density. If you look at the specification for modern graphics cards, you'll see that they can draw hundreds of thousands of triangles, and yet the small scene we were drawing has nowhere near that many triangles. The reason for the slowdown is that DirectX interacts with the graphics card. It turns out that, while the graphics card can draw a lot of triangles, you need to send them in large batches. Each time you call the Mesh.Draw method, it counts as a batch. Each tree you drew had four calls for the tree and 24 calls for the decorations. At 28 batches per tree, you soon push DirectX beyond the optimum amount (this number depends on many things about your hardware setup).

Which leads to this column's question: If it's not advisable to send multiple batches, how can you do animation of multiple objects like you see in popular games?

One way of doing this is actually to modify the mesh yourself. Rather than relying on the graphics card to move the vertices, you change them during the render loop. This technique is often used for particle effects and point sprites, but for this column I am going to build a prank screen saver in honor of the Coding4Fun April Fools' page. The screen saver will take a snapshot of the screen before it runs and then draw it back as a texture on the screen so that it looks like there is no screen saver at all. When the unsuspecting user moves their mouse or presses a key, the triangles that are actually making up the screen will break apart and fall to the bottom of the screen.

The first thing to do is to remove all of the DirectXmas code from the screen saver, or download CrashSaver_start from the links at the top of the article. This is the basic empty screen saver that we started with at the beginning of the last series.

Before DirectX initializes the screen, you need to get a capture of the screen.

In CrashSaver.cs add a method called Initialize and put the following code in it:

//Get a current snapshot of the screen before we draw on it!
//Code from http://tommycarlier.blogspot.com/2006/02/screen-capture-tool.html. Thanks Tommy.
Rectangle region = Screen.PrimaryScreen.Bounds;
screenBitmap = new Bitmap(region.Width, region.Height,
PixelFormat.Format32bppArgb);
using (Graphics bitmapGraphics = raphics.FromImage(screenBitmap))
{
bitmapGraphics.CopyFromScreen(region.Left, region.Top, 0,
0, region.Size);
}

Add a call to Initialize in the constructor for CrashSaver and create the texture in OnResteDevice:


snapshot = Texture.FromBitmap(e.Device, screenBitmap, Usage.None,
Pool.Managed);

Note   Due to an issue with the way .NET 2.0 interop works when run in the Visual Studio Debugger, and the fact that Managed DirectX relies on this issue for Texture.FromBitmap, you will find that this line of code can take many minutes to complete if you just hit "Run" or press F5. Until this issue is resolved, run this program without the debugger by pressing CTRL+F5. This performance issue does not occur when you run the final executable.

The next thing to do is to create the mesh that will eventually have all of the triangles animated. To keep it simple, imagine the screen split into a grid of squares and each square being split in half to make up two triangles. Because the animation is only going to be in two dimensions, you do not need to store all three dimensions. Instead you will use a vertex format that maps directly to the screen coordinates — more on that later. Constructing the mesh is simply a matter of walking through each cell and adding the six coordinates (three for each triangle) into a vertex buffer.

for (float x = 0; x < xResolution; x++)
{
for (float y = yResolution-1; y >=0; y--)
{
verts[vertIndex].X = cellWidth * x - .5f;
verts[vertIndex].Y = cellHeight * y - .5f;
vertIndex++;

verts[vertIndex].X = cellWidth + cellWidth * x -.5f;
verts[vertIndex].Y = verts[vertIndex - 1].Y;
           .... etc....

Because some of the vertices are common between the triangles, just copy the values rather than recalculate them. You will also notice that I have subtracted 0.5 from each coordinate: When DirectX looks into the texture maps to decide what color to output, it looks in the center of the pixel. The coordinates we are using have 0,0 at the top left corner and we want DirectX to look up pixel 0,0 for that pixel. So if we subtract 0.5 from each coordinate, DirectX will correctly look in the center of all the pixels. Try removing all of the -0.5s and see what happens.

In addition to setting the vertex position you need to set the texture coordinates. Remember that texture coordinates go from 0.0 to 1.0, so we have to divide that range across the whole screen.

           float texWidth = 1f / (float)xResolution;
float texHeight = 1f / (float)yResolution;

verts[vertIndex].Tu = texWidth * x;
verts[vertIndex].Tv = texHeight * y;
           ... etc ...

Since this is repeated six times, I am not going to list all of the formula here; you can look in the final version of the code to see them all.

The final thing to set up is the actual vertex buffer. Up till now you have always used a mesh object. A vertex buffer is just the part of the mesh object that stores the vertices. You saw it before in the texture articles where you modified the texture coordinates inside the vertex buffer. In this case, there is no need to use a mesh — you can just use the vertex buffer itself.

    //Copy to vertexbuffer 
vb = new VertexBuffer(typeof(CustomVertex.TransformedTextured),
bufferSize, e.Device, Usage.Dynamic,
CustomVertex.TransformedTextured.Format, Pool.Default);
vb.SetData(verts, 0, LockFlags.None);

Notice that I am using a vertex format called TransformedTextured. Textured you already know about, but transformed is new to you. This tells the graphics card that it does not have to do any transforming for you. In other words, you are providing the final screen coordinates. So the world, view, and projection matrices do nothing when this vertex buffer is rendered.

Rendering a vertex buffer is only slightly more complex than rendering a mesh (in fact, this is what Mesh.Render does for you when you call it).

   device.VertexFormat = CustomVertex.TransformedTextured.Format; 
device.SetTexture(0, snapshot);
device.SetStreamSource(0, vb, 0);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, bufferSize / 3);

Before you can run the code, there are few changes to make to the framework files. First, you need to ensure that the DirectX window is created at the same size as the screen and without a border. In dxmut.cs, in method CreateWindow(), comment out the following:

   //renderWindow.ClientSize = windowSize; 
In method AdjustWindowStyle() comment out the following:

   //window.FormBorderStyle = 
//System.Windows.Forms.FormBorderStyle.Sizable;

In dxmutdata.cs, in the constructor for GraphicsWindow add the following:

   this.FormBorderStyle = FormBorderStyle.None;
this.Width = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
this.Height =
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;

Now run the program (using CTRL+F5, remember) and you should see — nothing. What we are doing right now is just drawing a copy of the screen on the screen. If you hit a key the screen saver will exit and bring you back to Visual Studio.

To enable animation of the triangles, you need to create an embedded class inside CrashSaver that will store the data for each triangle's position and movement:

   private class TriangleMovement { public Vector2 offset; public
Vector2 velocity; public double delay; }

And then initialize this with some default values inside method Initialize().

  for (int x = 0; x < xResolution; x++) 
{
for (int y = 0; y < yResolution; y++)
{
for (int t = 0; t < 2; t++)
{
TriangleMovement move = new TriangleMovement();
move.offset = Vector2.Empty;
move.velocity = new

Vector2(0f, 0f);
move.delay = random.NextDouble() * 4.0;
moves[x, y, t] = move;
}
}
}

This code sets the offset position (which is how far down the screen the triangle will fall on the next frame and the current velocity of each triangle). There is also a random delay so that the triangles all fall at different times.

If you followed the original texture articles you will remember that to modify a vertex buffer you have to lock it before you can access the vertices. In exactly the same way that you modified texture coordinates, you will modify the y coordinate of the triangle to make them fall down the screen. Since you have seen this before I will not go into the code in detail. The interesting new part is how to calculate the velocity and offset.

  //Move the offset by the velocity 
move.offset += move.velocity * elapsedTime;
move.velocity.Y += (float)(9.8 * elapsedTime);

//3 vertices per triangle
for (int v = 0; v < 3; v++)
{
verts[vertIndex].X += move.offset.X;
verts[vertIndex].Y += move.offset.Y;
vertIndex++;
}

Because you want to stay frame-rate independent, remember that the offset from the last frame is the current velocity multiplied by how long it has been since the last frame.

You also want the piece to accelerate toward the bottom of the screen, which means you need to also increase the velocity. Once again, this is done as a function of the time since the last frame. If you want the triangles to accelerate faster, then make the constant bigger.

Now the screen saver should run. The final code can be downloaded from the link at the top of the article.

The code in this article uses Visual C# 2005 Express Edition and the February 2006 DirectX SDK.

The ZMan is here to solve your Managed DirectX programming problems. If you have a question for the ZMan then send it to zman@thezbuffer.com.

Credits:

Thanks to

Follow the Discussion

  • Mundi KingMundi King

    I have been testing these screensavers on xp professional and they work fine on preview mode, but when you actually use them and let the screensaver kick in, all you get for a screen capture is a blue screen.  (Note the triangle breaking code still takes effect, it just works with a pure blue screen)

  • mohanmohan

    how to change screen resolution automatically in vb.net and how to change the client resolution

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.