Multiple menu screens, crash

paiden

21-08-2007 22:25:33

I'm trying to use Multiple GUI's e.g. a MainMenuScreen and a OptionsMenuScreen.
Only one of this menus is shown at the same time.

My idea was the following. Each of this menus gets its own sheet and all the widgets belonging to a menu are children of this sheet.

When the MainMenu is activated i call GuiManager::setActiveSheet() with it's sheet, show it, and hide the sheet of the Options menu. If i wan't to see the OptionsMenu i activate it's sheet, call Sheet::show() and hide the sheet of the MainMenu

This works so far. But when the Menus have switched and a MouseMove is injected, the app crashes at the following lines of QuickGUIManager (~399):

Widget* wItr = mWidgetContainingMouse;
while( ((hitWidget = wItr->getTargetWidget(args.position)) == NULL) && (wItr != NULL) )
wItr = wItr->getParentWidget();


What should i do to get things to work?

kungfoomasta

22-08-2007 00:00:44

Sorry if I asked this previously.. are you using the most recent version?


void GUIManager::setActiveSheet(Sheet* s)
{
if( s == NULL ) return;

std::list<Sheet*>::iterator it;
for( it = mSheets.begin(); it != mSheets.end(); ++it )
(*it)->hide();

mActiveSheet = s;
mActiveSheet->show();

// Update the active widget
mActiveWidget = mActiveSheet;
mWidgetContainingMouse = mActiveSheet;
injectMouseMove(0,0);
}


When you set an active sheet, it will hide all sheets and show the active one, so you don't need to worry about it. Your idea with different sheets is the same idea I had when making the sheet widget. :wink:

The only thing I can think of for the error you are seeing is that mWidgetContainingMouse is NULL. You'll have to debug this to confirm, or maybe I can try to switch sheets and reproduce this. According to the function above, I set the mWidgetContainingMouse to the newly active sheet, and then call mousemove(0,0) to see if the mouse is over another widget.

[edit]
I was able to switch the atctive sheet and did not have any problems.

I modified the QuickGUIDemo, so that when you press the login button it changes sheets:


bool evtHndlr_login(const QuickGUI::EventArgs& args)
{
/* Ogre::UTFString s;
if( usernameTB->getCaption() == passwordTB->getCaption() )
s = "Login Successful.";
else
s = "Username and/or Password do not match.";

loginResultLabel->getText()->setCaption(s);
*/
QuickGUI::Sheet* newSheet = mGUIManager->createSheet();
mGUIManager->setActiveSheet(newSheet);

return true;
}


All widgets disappeared accept for the mouse, and I was able to move the mouse around.
[/edit]

paiden

22-08-2007 08:54:09

No, i'm using v9.5.2

When i set the Active sheet and dont hide the other one, both sheets are shown.

I tried Version 9.5.6BETA
But i get an Ogre item identity exception at the following line:

guiSystem_ = new QuickGUI::GUIManager(window->getWidth(), window->getHeight());


The exception says:
Cannot find codec for exception material

What does that mean?

kungfoomasta

22-08-2007 09:08:36

I don't recall ever seeing and error like that..

It could be that the GUIManager is trying to create the mouse cursor with the default skin, "qgui.pointer.png", and you do not have it listed as a resource. Are you able to step into that function to narrow down the scope of the error?

[edit]
It could also be a problem with mismatching dll and lib. Make sure your QuickGUI.dll matches v0.9.6 .lib. You can try removing QuickGUI and rebuilding it and adding it to your project, using v0.9.6 beta.
[/edit]

paiden

23-08-2007 18:15:01

I don't recall ever seeing and error like that..
Are you able to step into that function to narrow down the scope of the error?


The exception raises here (QuickGUISkinSet ~70):

for( texNameItr = mTextureNames.begin(); texNameItr != mTextureNames.end(); ++texNameItr )
{
Ogre::Image tempImg;
tempImg.load((*texNameItr),Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
images.push_back(tempImg);
mContainedTextures.push_back((*texNameItr));

++mNumIndividualTextures;
}


And i've found the problem. E.g all my qgui Files are in a folder called gui.
But there are also other files like *.material or *.svn (for subversion). QuickGUI tries to load everything contained in that folder including the *.material files and the *.svn directory causing the exception.

kungfoomasta

23-08-2007 19:32:47

Yah, that will happen. I wonder if there is any easy way to implement a function such as:

bool isImage(const Ogre::String& ImageFileName);

I could hard code it to accept .png, .jpg, etc. but this would disallow use of images without extension, which are currently supported. I'm open for suggestions, but for now I would suggest making your skin name unique and only used for skin images.

paiden

23-08-2007 20:55:26

The only thing that's interesting is, that this didn't happen in v9.5.2

Zini

23-08-2007 21:07:30

I am a bit surprised, that QuickGUI can handle images without extensions (we are talking here about something like a jpeg file without the .jpg extension, right?). From what I see in the code, it should not be possible. You are still using Ogre::Image, right? AFAIK Ogre::Image is based on Ogre's image codec system, which does not support extension-less files.

kungfoomasta

23-08-2007 22:09:42

Oh, I did not know that, sorry for my ignorance. Well in that case, I can easily make a function that will see if the end extension is a supported format. I'll add this in.

The only thing that's interesting is, that this didn't happen in v9.5.2

As you have problably read and heard, v0.9.6 is not using materials. Materials are defined in scripts, and through overlays, add lots of unecessary batching. We are using images now, such as .png, .jpg, whatever Ogre can recognize and handle. So if each unique texture will add a separate batch to the render queue, how do we solve this problem? We create a texture atlas, known as the QuickGUI::SkinSet class. Basically you call GUIManager::loadSkin("qgui") for example, and it will create a SkinSet based on qgui images in your resource paths.

qgui.button.png
qgui.window.png
etc.

The SkinSet is created and used behind the scenes, allowing you to still create individual textures. In fact, you can use textures for widgets that are not a part of any skin, however they will add a batch to the renderer.

Hopefully this sheds light and understanding on the issue.

kungfoomasta

23-08-2007 22:21:28

This is how I retreive the files used for the skinset:


void SkinSet::_findSkinTextures()
{
mTextureNames.clear();

Ogre::ResourceGroupManager* rgm = Ogre::ResourceGroupManager::getSingletonPtr();
Ogre::StringVector resourceGroupNames = rgm->getResourceGroups();

Ogre::StringVector::iterator groupItr;
for( groupItr = resourceGroupNames.begin(); groupItr != resourceGroupNames.end(); ++groupItr )
{
Ogre::FileInfoListPtr files = rgm->findResourceFileInfo((*groupItr),mSkinName + "*");

for( Ogre::FileInfoList::iterator fileItr = files->begin(); fileItr != files->end(); ++fileItr )
{
Ogre::String fileName = (*fileItr).filename;
if(_isImageFile(fileName))
mTextureNames.push_back(fileName);
}
}
}

bool SkinSet::_isImageFile(const Ogre::String& fileName)
{
Ogre::String::size_type index = fileName.find_last_of('.');
if( index != Ogre::String::npos )
{
Ogre::String extension = fileName.substr(index,fileName.length() - index);
if(
(extension == ".png") ||
(extension == ".jpg") ||
(extension == ".bmp")
)
return true;
}

return false;
}


I will make a post giving SVN info for read only access.

Zini

23-08-2007 23:15:16

Hardcoding the extensions is a bit inflexible, especially since Ogre is maintaining a list of known extensions. How about this:


bool SkinSet::_isImageFile(const Ogre::String& fileName)
{
Ogre::String::size_type index = fileName.find_last_of('.');
if( index != Ogre::String::npos )
{
Ogre::String extension (fileName, index+1);
Ogre::StringUtil::toLowerCase (extension);

Ogre::StringVector result (Ogre::Codec::getExtensions());

return result.find (extension)!=result.end();
}

return false;
}


(untested code, sorry for that)

paiden

23-08-2007 23:33:24

The only thing that's interesting is, that this didn't happen in v9.5.2
As you have problably read and heard, v0.9.6 is not using materials.

Nope, didn't hear that yet.


Basically you call GUIManager::loadSkin("qgui") for example, and it will create a SkinSet based on qgui images in your resource paths.

OK. But the problem is, that it doesn't take the images only, it takes all resources contained in the directory.

Hopefully this sheds light and understanding on the issue.
No, not really. Maybe because of my bad English. Sorry :(

I hope you don't misunderstand me. I think QickGUI has the potential to replace GUI-systems like CEGUI and many others (I've tried about 4 GUI systems before and QuickGUI is the best so far), and you have done well so far, but there is alwas something to improve. So please let me give you some suggestions:

* When you make such major API-changes, that means that your new relese can't be used like the old one, change the version number in another way e.g. i would use a version number of 1.0.0BETA for this release (because the usage of the API changed, going from 9.5.2 to 9.5.6 means some bug fixings for me, nothing more).

* Make better documentation. Documentation is the essential thing for every Library and the documentation for QuickGUI is really lacking.
The demo is nice, but it isn't able to clear the questions users of your Library have.

I really hope you won't be offended by the things i said here. But the reason is, I think QickGUI has really much potential and I want to help you with increasing that potential even further (or farther, my bad English :().

I hope we will be discussing much more.

PS: Oh my god, my english is so bad :(

kungfoomasta

24-08-2007 00:10:00

Sorry for my previous response.. I put in a lot of effort to put out the release, so I'm a little tired today, hehe.

It was a pretty significant change from 0.9.5 to 0.9.6. There shouldn't be any more massive interface changes, I hope. That's why I went ahead and added the Rect, Point, and Size classes now, rather than later.

The skinset should browse through resources, and only use supported images. Thanks for the snippet Zini. I had to make some changes, but I got your logic down. I think you made a small mistake getting the extension, you have to get a substring of the last portion of the text, not the beginning. Also, vector doesn't support a find operator, so I had to iterate through and compare. Here is the code:


bool SkinSet::_isImageFile(const Ogre::String& fileName)
{
Ogre::String::size_type index = fileName.find_last_of('.');
if( (index != Ogre::String::npos) || (index = fileName.length() - 1) )
{
Ogre::String extension = fileName.substr(index + 1,fileName.length() - index);
Ogre::StringUtil::toLowerCase(extension);
Ogre::StringVector supportedImageCodecs(Ogre::Codec::getExtensions());

Ogre::StringVector::iterator it;
for( it = supportedImageCodecs.begin(); it != supportedImageCodecs.end(); ++it )
{
if((*it) == extension)
return true;
}
}

return false;
}


Thanks for your input, kind words, and help guys, I really appreciate it. :)

[Edit]
I was wondering why my batch count was so high. I changed the code above, the supported codec are strings like: "png" and not ".png"
[\Edit]

Zini

24-08-2007 07:38:01

You are right about the find method. That is why I dislike a too liberal use of typedefs. In the back of my mind, I was thinking, that StringVector was one of the STL containers, which does support find. Sorry, it was way past midnight in my timezone, when I was writing this and most of my brain was already at sleep.
The obvious solution would have been to use std::find instead. But your for loop construct is doing the job as well.

Though I think I have to defend my string construct. Quoting from »The C++ Standard Library«, by Nicholai M. Josuttis:

string s (str, stridx) Creates a string s that is initialized by the characters of string str starting with index stridx.

kungfoomasta

24-08-2007 09:23:57

Cool, I learn new things all the time! Well either way works, so unless you really want it using the method you originally suggested I'll just leave it as is... :lol:

Zini

24-08-2007 11:53:38

It's OK. Doesn't really matter, though I would remove the »fileName.length() - index » argument from the substr call, because it is completely redundant.

A plain

substr (index)

produces a string from the given index to the end of the original string.