Ask the ZMan: A Holiday Screen Saver with DirectX
- Posted: Oct 31, 2006 at 10:48 PM
- 491 Views
- 3 Comments
Loading User Information from Channel 9
Something went wrong getting user information from Channel 9
Loading User Information from MSDN
Something went wrong getting user information from MSDN
Loading Visual Studio Achievements
Something went wrong getting the Visual Studio Achievements
|The project is a holiday-themed screen saver called DirectXmas.|
Time Required: 1-3 hours
The Coding4Fun team asked "Can we have some holiday-themed Coding4Fun articles?"
Of course it's important to answer questions from the readers too, so over the next few articles I will build a project that answers some questions and keeps the Coding4Fun "powers that be" happy. Here are some of the questions I will cover:
The project will be a holiday-themed screen saver that I imaginatively called DirectXmas (not because I want to recognize any particular holiday celebration over another, but purely because I get to use the X in DirectX twice). I will use the texturing techniques that I explained in the previous articles to animate a series of Christmas trees and other holiday decorations.
Creating the Screen Saver
As luck would have it, C# and VB.Net Express Editions both ship with a screen saver starter kit (Figure 1).
Figure 1. The Screen Saver Starter Kit project type in C#.
Reuse is just as important in tutorials as in code, so for instructions on how to create the project in the starter kit head over to Andrew Coates' weekend warriors column, also on Coding4Fun. You don't need to follow the entire article (unless you want to, of course). Just go as far as creating the project and running it.
The project will be using the DirectX sample framework code and code from previous articles so add references to Microsoft.DirectX, .Direct3D and .Direct3DX. Browse to the sample framework source (usually C:\Program Files\Microsoft DirectX 9.0 SDK (October 2005)\Samples\Managed\Common) and add the files that match dx*.cs (avoid the files beginning with w; they are beta files for a future DirectX SDK). Finally, add the Media folder and the three source files from the last texturing article. Solution explorer should look like Figure 2.
Figure 2. The project start point.
If you try to compile at this point, you will get two errors. The first one complains about unsafe code. The DirectX Framework has some unsafe code in it for performance reasons. You enable this in the project properties as shown in Figure 3.
Figure 3. Enabling unsafe code for this project.
The second error says that there are two entry points—which makes sense, because at the moment there are two projects merged together. Since the screen saver sample has some useful code in it, you should leave that as the main entry point and rename the one in Texture.cs:
static int Main()
public static int DirectXmas()
Everything should now compile and run and you should still see the default screen saver from the sample.
Making the Screen Saver use DirectX
Take a look in the program.cs source file, specifically at the Main() function. Screen savers are just normal .EXEs which have been renamed to .SCR. When they are run, a command-line parameter is passed to tell them if they are supposed to run as normal, show the options dialog or show in the preview window. The screen saver runs when the command line is /s or when there are no parameters. The code is in the ShowScreenSaver() function. To call the old entry point and run our DirectX code, replace the 2 lines of code in that routine with:
Run the program now and you will see the textured earth from the last tutorial. But wait, that's really not how screen savers are supposed to look. Firstly, it's in a window, and secondly, it doesn't close when you move the mouse or press a key.
The sample framework makes it very easy to create a full screen application. Find the line that creates the DirectX device:
sampleFramework.CreateDevice( 0, true, Framework.DefaultSizeWidth,
then change the "true" to "false". The second parameter controls whether the device is created in a window. You probably notice that it's running at a low resolution, since the default height and width are 640x480. This is something that could be a setting in the screen saver settings screen, but for now using the current screen resolution is acceptable. Change the code to read:
The program now runs full screen; however, you can only get out by pressing ESC.
Open the source code for the sample screen saver ScreenSaverForm.cs to see how they check for the mouse and keyboard. The KeyDown and MouseDown events just close the form and quit the program. The MouseMove event is a little more complex. The first time it is called, the code stores the position of the mouse. On subsequent calls it checks the mouse to see if it has moved significantly from the stored point. In this case significantly means more than 10 pixels in any direction. I'm not 100% sure why the code is like this, but I suspect it just stops desk tremors and minor earthquakes from cancelling the screen saver (vital on the West coast of the USA).
If you have looked at any of the DirectX samples in any detail, you will know that although you see a form, the code does not in fact inherit from a Windows form, so that code cannot be used unchanged. Fortunately, the sample framework provides similar functionality.
If you are still looking at the CreateDevice() call above, then look just one line higher to where the application hooks the KeyDown event.
You should add two more event handlers for the MouseMove and the MouseDown events:
The new OnMouseDown() function looks like this:
private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
GraphicsWindow window = (GraphicsWindow)sender;
And you should replace the OnKeyEvent() code with the same two lines.
Using the screen saver sample as an example, the OnMouseMove() function becomes:
private bool isActive = false;
private Point mouseLocation;
private void OnMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
// Set IsActive and MouseLocation only the first time this event is called.
mouseLocation = e.Location;
isActive = true;
// If the mouse has moved significantly since first call, close.
if ((Math.Abs(e.Location.X - mouseLocation.X) > 10) ||
(Math.Abs(e.Location.Y - mouseLocation.Y) > 10))
GraphicsWindow window = (GraphicsWindow)sender;
Running the program now gives the desired effect.
If you have been following along building the code from the last tutorial there is a little "bug" to fix from last time. The code has a hard coded path to the earth texture which won't work when the screen saver is in the system32 directory where screen savers live. You can use the sample framework Utility.FindMediaFile() function to help with locating this. If you look at the source for that function it searches a set of "likely" paths until it finds the named file. In texture.cs, OnResetDevice() replace the code that loads the earth texture with the following:
//Load the textures
sphericalTexture = TextureLoader.FromFile(e.Device, Utility.FindMediaFile("earth2k.jpg"));
To make the program work as a screen saver there are a few manual steps to follow:
Now right-click on your desktop and choose Properties, then select the Screen Saver tab (Figure 4).
Figure 4. Choosing the DirectXmas screen saver.
Select DirectXmas and press preview to see the result of your work. There's no screen shot because the screen looks exactly like it did at the end of the last tutorial; it just works as a screen saver.
The code is a long way from being finished:
Stay tuned and if you have suggestions please let me know.
Homework for this time (though I've never had anyone send me an answer yet!):
The ZMan is here to solve your Managed DirectX programming problems. If you have a question for the Zman then send it to firstname.lastname@example.org.