The project timeline is going to need a complete rewrite. No, we're not behind schedule... some things just need to be switched around. More on this later. First let's get some small changes out of the way.
SampleContext now checks required plugins, renderer, and runs a capabilities test before running a sample. If a required plugin is not found, an exception is thrown. Sample::testCapabilities can throw whatever exceptions it wants, if at all. If the sample requires a specific render system to run, and that render system is not currently in use, it will also throw an exception. The original plan was for the browser to do preliminary testing of all samples and mark the failed ones. This could get quite slow if done for every sample, and I'm not so sure anymore that marking samples as "out of order" will be so useful. The new plan is that the browser will do exactly what the SampleContext class does, but it catches the first exception thrown, displays it, and goes back to the menu without crashing. Sample::testCapabilities now takes a RenderSystemCapabilities pointer. This is retrieved from the RenderSystem by the SampleContext and passed to the sample at testing time. The reasoning was that a capabilities test
almost always needs this object, so it would be convenient to have it already provided.
Everything that used a SampleQueue now uses a SampleSet. SampleSet is an std::set that ensures every sample is unique, and sorted by name using a utility Sample::Comparer structure. The sample browser now keeps a master set of samples, as well as a set of sample categories. This ensures that both categories
and samples are unique and sorted alphabetically. Samples get a default title ("Untitled") and category ("Unsorted") if none are found. Of course, this means that the SampleContext no longer has sample queueing capabilities which, in retrospect, were quite useful for testing, but not so much for the user. So as before, you'd provide an optional initial sample to SampleContext::go.
The browser now creates a dummy scene and camera whenever a sample is not being run. This ensures that the browser can render its own GUI even when no sample is running. Of course, this could also be used to create some "menu scenery" like in Half-Life 2, but that'd add to startup time, so... no.
And now, the
big news: the arrival of a new sample GUI system.
I think we can all agree that OGRE's overlay system does not make for much of a "GUI" on its own (at least not a very advanced one, and definitely not interactive). If you're making your own game, chances are you'll end up using a separate GUI solution like CEGUI, QuickGUI, MyGUI, just to name a few. But when you're making samples or 3D demos and you want to quickly add some buttons or slider bars, the overlay system is just not enough, and the other GUI solutions are a bit overkill. Most GUI solutions require some kind of "renderer" in order to interface with OGRE. They also have static libraries you need to link to and one or more dynamic ones. The current SDK has 4 dlls in the runtime folder just for CEGUI, and 23 files in the gui media directory. There's .xsd files, .layout files, .config files, .scheme files, .imageset files, .looknfeel files, .xml files, .font files, and .tga files. On the code side of things, setting up and using CEGUI is not a task I'd want to do for just a sample. Does it really make sense for Gui.cpp (the SDK GUI sample) to be 569 lines of code? I mean even DeferredShadingDemo.cpp only has 547. What the sample framework needs is a middle ground between these two extremes...
This new system was designed to be minimalistic wherever possible, and it does so not by looking at
what a GUI needs, but instead at
what a GUI has that most samples probably won't use. Are you really going to reskin your sample GUI and apply different themes? Are your buttons really going to have different heights? Do you really need to provide callback delegates separately to individual widgets? Does your sample really need tons of windows and tabs? Are your sample GUIs really going to be so complex that you need an external editor to export a layout file of some sort? Let's say you want to setup a simple quit button for your sample... what if all you had to do was this? And I mean
all you have to do...
Code: Select all
mQuitButton = mGui->createButton("QuitButton", "Click Me!");
To respond to your button's events, your sample just has to extend GuiListener and make the following callback:
Code: Select all
virtual void buttonClicked(Button* b)
{
if (b == mQuitButton) mDone = true;
}
Where should you put your button? Well, in most samples, widgets are off in the corners or to the sides, and rarely, a text box could be turned on in the center to display instructions. Also, since text flows sideways, buttons are wider than they are tall, and are usually stacked on top of each other, instead of side to side. This also goes for slider bars and drop-down menus. Taking into account these observations, the new system provides a quick and intuitive way to organize your widgets while being flexible enough to handle all kinds of layouts. Observe.
Code: Select all
mGui->moveWidgetToTray(mQuitButton, TL_BOTTOMRIGHT);
The GUI provides 9 convenient "anchor points" - 1 in each corner, 1 on each edge, and 1 in the center. At each of these anchor points, there is a widget "tray" that holds your widgets. A tray only shows up when you have at least one widget in it, and it forms a bubble that wraps neatly around all the widgets you put in it. Here's a screenshot to demonstrate the power of these widget trays:
I placed 2 buttons in the top left tray, 2 in the left tray, 3 in the center tray, and 2 in the bottom right. Notice how they make a transparent wrap around each group of widgets. Here's two more screenshots showing how the shapes of the trays change as I insert more widgets. They also show how the trays look on different background colours.
The trays wrap around all of your widgets perfectly, regardless of their horizontal alignment. You can customise the tray padding (distance of the trays from the edges of the screen), widget padding (distance of widgets from edges of the trays), and widget spacing (distance between widgets in a tray). Trays along the top of the screen grow downward. Trays along the bottom grow upward, and trays in the middle grow both ways vertically. At any time, you can insert or remove a widget anywhere in a tray, and the tray will automatically update its shape or hide itself, depending on the new contents. Use of the trays is optional. You can have your widgets free floating.
You can make as many GUIs as needed. Each GUI comes with 3 layers - the backdrop, the widgets, and the cursor. You can toggle the visibility of all layers. When showing the backdrop, you can specify a material to fill the backdrop, or if you don't, the last material will be used. If either the cursor or the widgets are hidden, GUI events do not fire (there's either nothing to press, or nothing to press with). So far, the following widgets are planned: Button, StatsPanel, CheckBox, TextBox, Dropdown, Slider, SectionHeading. You can easily extend your own widget. Also, you can still access all the underlying overlay elements of the GUI system to make any tweaks you want.
This GUI requires no libaries to link to (static or dynamic), no resources you have to create, and no scripts you have to write. If you're using SdkSample, you're already good to go. If you're using Sample directly, just include "BasicGui.h". All the resources used by the GUI system are in OgreBites.zip, which is loaded by the browser on startup. Its contents consist solely of an overlay script, a material script, textures, and fonts - all familiar OGRE file types. The GUI system has no external dependencies as it is completely based on OGRE's overlay system.
OgreCore.zip is now (or will soon become) obsolete. The debug panel will be replaced by the StatsPanel widget, which will be in the bottom left tray by default in every SdkSample. The rest of OgreCore.zip's contents will be replaced by procedural content in the near future. For now, it will be kept for backwards compatibility, but it is no longer required by the framework at all.
Just as the samples will use the new GUI system, the browser will use it as well for its menu and configuration screens. The "Browser" and "Common" resource groups have been renamed to "Essential" and "Popular", respectively. The "Essential" group contains OgreBites.zip and the thumbnails directory. These are necessary to run the browser, and most of the samples. The "Popular" group will contain the most commonly used sample resources. I think these group names are much clearer and more descriptive.
Another implication of this new GUI system is that many of the keyboard toggles and value up/down keyboard shortcuts in the current SDK samples will be removed and replaced with GUI controls. Input device control can be easily toggled between the GUI and the scene. In addition to using the BasicGui system by default, SdkSample will also include a default camera controller, called CameraMan. This is basically the camera utility class mentioned in previous weeks.
Keep in mind that although SdkSample, SamplePlugin, SampleBrowser, BasicGui, CameraMan, and OgreBites.zip are used extensively by the SDK, they are not part of the core samples framework which consists only of Sample and SampleContext. In fact, BasicGui, CameraMan and the contents of OgreBites.zip may be used by any of your OGRE applications without using the samples framework at all.
So, now you can see why the project timeline has to be rewritten. In order to develop many of the browser's features, it would make sense to have a GUI system in place to test it first. So for the next couple of weeks, I will develop the essential GUI widgets and some of the more important browser features. Hope you guys are in favour of this new system I've introduced. Feel free to make suggestions or ask questions! Cheers!