I had a day off from work on Monday. I’m so rested right now that I feel tired because of it.
Once in a while, especially after a longer post, I’ll come back and revisit it. I’ll do this to clarify some points, list some alternative techniques, do some natural follow ups or anything else that does not introduce a new concept.
This is what I’ll be doing right now for lesson number one. If you look over most DXUT (and even straight DirectX) examples out there, they are a huge pile of spaghetti code, with a bunch of global variables and a few functions. I would like to add a little more OOP styled structure to it. You may also notice that a large amount of code remains completely unchanged from sample to sample. I would like to both create a minimal framework that allows the reuse of these snippets and to put it into action to make my samples leaner (and meaner, but not like that macro).
But keep in mind that what I am trying to do is not a wrapper. I do not want to introduce another layer on top of what is already a layer on top of DirectX. I just want and an OOP structure to DXUT styled application and add a little code reuse. What I’ll be using will serve exactly this purpose and nothing more, so don’t expect good and flexible design, qualities that would recommend this approach for anything more than small tutorial applications or anything that hides the DXUT approach an turns it into a high(er) level abstract CG framework.
Today I am only offering a preview of this approach. I’ll take the example from my last post and show how it looks after the changes. I’ll give the same treatment to lesson 2, so I’ll explain a second official tutorial as is and in the follow up show how it can be done in a more OOP style. Only in lesson 3 will I formally introduce the classes that are involved and post full source code for them. Until then, here is a preview for the DXUTApp class:
It is a very simple class that mirrors the callback approach from DXUT and has some very basic functionality implemented for each callback. Go back to lesson number one to see what these callbacks are used for if you need to. Their name has been cleaned up a little and I hope you’ll have no problems matching things up. Let me list the default functionality of these methods:
- IsDeviceAcceptable will simply return true, so all devices are accepted.
- ModifyDeviceSettings returns true, so the application will accept any settings.
- OnCreateDevice will return S_OK, thus not creating or modifying any resources. The same goes for the 3 other resource creation/destruction functions.
- OnResetDevice will return S_OK.
- OnLostDevice will return S_OK.
- OnDestroyDevice will return S_OK.
- OnFrameMove does nothing.
- OnMessage returns 0 and does not process any message.
- OnFrameRender will clear the back buffer with the color black and clear the Z-buffer.
These member functions take the same parameters and have the same return type as their non member counterparts, except for the void pointer used for custom data. If you need custom data just place it into your class that inherits from the DXUTApp class. In practice I have not encounter a single example yet where this would not be enough and you needed to have that extra parameter.
The class also has a Register method. You need to call this once and should be one of the first things you do in the main method.
Using this class you could simply instantiate as a local variable in your main function, call register and then proceed with the normal DXUT setup functions. You don’t need to use any of the DXUTSetCallbackXXX methods and using them after the Register call will actually break the functionality of the class. Doing this will net you with a black window that should run OK on pretty much any computer known to man (if it has DirectX 9 support).
But we are not going to do this. We are going to take it one step further and inherit from this class and override some functions so we get an example that has exactly the same functionality as the “EmptyProject” SDK sample:
We inherit from the DXUTApp class and only override the methods we need to. In practice this means that in real examples we’ll override every single method. On the other hand, after 2-3 lessons we’ll isolate some code that is repeated and stick it into a class and start inheriting from that class to reduce code repetition. Let’s take a look at the implementation of these methods:
The code for IsDeviceAcceptable is 100% identical to the one from the original sample. In fact I just copied it over, this is why I did not rename the parameters to a slightly more reasonable and uniform scheme. I did this intentionally to show that you can take existing code and fit it into my scheme. This is vital I think for beginners. Once you get the hang of DXUT you’ll have not problem switching things around as much as you please. Anyway, the function does the same alpha blending support test to exclude some devices from running this application. Soon we’ll exclude everything that does not support pixel shader 2.0 and once we target pixel shader 3.0 the pixel shader 2.0 code paths will be treated as fallback.
The second function we override is the rendering one. Since the default implementation from my class fills the frame with black and our original sample did so with blue, we need to override it. As explained earlier, this is not meant to be a full framework that you would use in let’s say the implementation of a serious project, like a commercial game. Under such circumstances you would probably add support for background colors and even a full stack of modular post processing effects. Here we just copy the code and change the color:
The final piece of the puzzle is the main function:
The function pretty much looks the same, but instead of registering a bunch of callbacks we just register our instance of the class. Even though this is not meant to be a wrapper, the base DXUTApp class will get some minor improvements as time goes by. As an example, for me personally the use of “DXUTMainLoop();” is quite jarring. Since we are doing things OOP and already have a local variable called “app”, invoking the main loop should look something like “app.MainLoop();”.
But this will be sufficient for our first preview of the OOP approach. I need to save something for later.
As I reminder, for people who skipper the first post from this series because it was too long, let me reiterate the purpose of the series: I want to share a few useful shaders that took me quite a while to piece together from dozens of sources on the Internet. Finding good and easy to understand shader information out there is quite the scavenger hunt.
Meanwhile I have decided to add about 3 more lessons on the subject of terrain rendering as a bonus. While creating the procedural terrain renderer I tried a few techniques that are reasonable and can be useful but did not fit my particular needs. I want to preserve them too.
So that’s the basic idea: if I spent hours trying to piece together a shader that in the end I did not get to use, I’ll post it here (but only if I can do it in a comprehensive way) before it gets lost somewhere on my hard drives and I forget how they actually work.