I am trying to devise a way of resizing/scaling text so that I can have a fully scalable UI. In fact, I already have almost everything having to do with ui scaling already finished. Everything except text.
As we all know, you can only change the font to be used with static text. There isn't a fontSize member or anything.
What I would really like to do is find some way to convert or render a IGUIStaticText to an ITexture. I could then easily make an IGUIImage out of that ITexture, scale it however I want and the engine won't even have to be rescaling or recreating it on every frame.
There doesn't seem to be a way to do this.
I know about Irrlicht Example #13 - RenderToTexture. That sounds like a great idea! The only problem is that there isn't really a way to render only the gui text to a texture. Where would I position the camera for that since it's ui? Is it possible to do this for only the selected static text or can it only render the entire screen to the texture?
I can't figure out a way to use RenderToTexture for just UI text. I'm not even convinced that this is going to be any more versatile from just having 256 different image files, one for each character and then making a new igui text class that creates then returns an ITexture based on a string input that it constructs from those character sprites. I guess if it turns out you can't just manually change ITextures like that the same way you just can't manually get pixels from static text then this plan is dead too.
Any advice? Is there something I'm missing?
How do I either resize static text OR draw static text to an ITexture?
-
- Posts: 16
- Joined: Wed Jun 26, 2024 12:28 am
Re: How do I either resize static text OR draw static text to an ITexture?
Phew, there are some ways to do that and I'll get to them in a moment, but let's first say - this is usually not going to work well. Scaling fonts by scaling the texture will look mostly horrible. Other solutions which I'd rather recommend:
a) Use several versions of a font with different sizes and switch between them.
b) Use ttf - that is the solution I tend to use. It's basically same as a) but at least slightly more compfy. See CGUITTFont.cpp/.h (the font implementation) and ttf.cpp (an example how to use it) on https://github.com/mzeilfelder/irr-playground-micha for that. It's using the freetype library so you'll have to add that as well to your project. And that allows getting a font with a certain size so it's easier to switch. And as it directly renders that font in the requested size it will look way, way(!) better than scaling an image.
edit: How to do a scalable UI is a bit of an art. For many cases IGUIElement::setAlignment is your friend. Which controls how UI element behave when you resize them. Font resizing depends on the situation. I usually avoid font changes when the window resizes. I do on the other hand put in quite some work in all UI elements for DPI changes. In some projects when I build dialogs _every_ element has some dpi scaling factors. Which with Irrlicht means - on DPI changes (for example people moving my app to a new screen) I rebuild all dialogs - and that includes replacing fonts.
Now for your original question. For simple overhead display stuff you can often use IBillboardTextSceneNode. Which just prints some scaled text based on the distance it has to the camera. So yeah - that one is scaling textures after rendering the font and it can be acceptable for some things like hit numbers over a character. But I wouldn't ever use this for UI texts.
And as you guessed - you can do the same with RenderToTexture. When you set a render-target-texture as the active render target then everything you render usually to the screen goes into that texture instead. Example: You can then draw some UI elements (like IGUIStaticTexts) by calling draw() on them directly. Or you can clear/hide everything else in the GUI and call IGUIEnvironment::drawAll(). Or - simplest solution for this - you can even directly call the draw functions of a font with some text. Afterwards switch back to the screen render target and you can now use that texture any way you want.
edit: Also in case it wasn't clear - you don't need to point the camera for gui -> render to texture. Just works the same as when drawing to screen.
a) Use several versions of a font with different sizes and switch between them.
b) Use ttf - that is the solution I tend to use. It's basically same as a) but at least slightly more compfy. See CGUITTFont.cpp/.h (the font implementation) and ttf.cpp (an example how to use it) on https://github.com/mzeilfelder/irr-playground-micha for that. It's using the freetype library so you'll have to add that as well to your project. And that allows getting a font with a certain size so it's easier to switch. And as it directly renders that font in the requested size it will look way, way(!) better than scaling an image.
edit: How to do a scalable UI is a bit of an art. For many cases IGUIElement::setAlignment is your friend. Which controls how UI element behave when you resize them. Font resizing depends on the situation. I usually avoid font changes when the window resizes. I do on the other hand put in quite some work in all UI elements for DPI changes. In some projects when I build dialogs _every_ element has some dpi scaling factors. Which with Irrlicht means - on DPI changes (for example people moving my app to a new screen) I rebuild all dialogs - and that includes replacing fonts.
Now for your original question. For simple overhead display stuff you can often use IBillboardTextSceneNode. Which just prints some scaled text based on the distance it has to the camera. So yeah - that one is scaling textures after rendering the font and it can be acceptable for some things like hit numbers over a character. But I wouldn't ever use this for UI texts.
And as you guessed - you can do the same with RenderToTexture. When you set a render-target-texture as the active render target then everything you render usually to the screen goes into that texture instead. Example: You can then draw some UI elements (like IGUIStaticTexts) by calling draw() on them directly. Or you can clear/hide everything else in the GUI and call IGUIEnvironment::drawAll(). Or - simplest solution for this - you can even directly call the draw functions of a font with some text. Afterwards switch back to the screen render target and you can now use that texture any way you want.
edit: Also in case it wasn't clear - you don't need to point the camera for gui -> render to texture. Just works the same as when drawing to screen.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
-
- Posts: 16
- Joined: Wed Jun 26, 2024 12:28 am
Re: How do I either resize static text OR draw static text to an ITexture?
Thanks, I didn't know about the draw function. I was able to use that to come up with code that draws static text to a IGUIImage and then scales it up. It looks like crap, you were definitely right about that. Getting this method of text drawing to work with text boxes and list boxes would be difficult because it makes everything more complicated anyway.
This CGUITTFont class is really good. I can generate a bunch of different fonts and then simply use them with SetOverrideFont(). I set things up so I have a database object full of every even number font size between 10 and 60, then I use ui resizing code to select the correct font based on screen size.
One thing I haven't figured out yet is how do I set the override text of a IGUIListBox? It doesn't have a SetOverrideFont member function and every method i've tried of getting the listbox's children so I can apply SetOverrideFont to them have not worked out.
For example this doesn't work. All it picks up is 1 IGUIScrollBar and no text labels.
I haven't tried getItemAt() yet, the one where you have to give it an x and y value and it returns whatever gui element is at that screen coordinate. Doesn't seem like that great of a way to get elements but if its the only way that works then fine. I'll have to make some loop that polls all the vertical pixels looking for unique ui elemtns I suppose. Edit: tried that, it didn't work. It still can't get select the text elements. I also tried adding a function to the IGUIElement class that returns a non-const reference to core::list<IGUIElement*> Children;. This still didn't work because there's nothing except a scroll bar in that list.
Where is the content of the IGUIListBox class anyway? The header file doesn't contain enough code and there isn't a IGUIListBox.cpp file anywhere. I tried making my own IGUIListBox class from IGUIListBox.h and CGUIListBox.cpp but now there's 2 problems I can't figure out.
First of all, I can't find any reference to "os.h", I can't find it on my system and I can't find anything relevant about it on the internet. I am able to compile irrlicht for some reason but I don't have os.h or <os> or whatever it's supposed to be called. I haven't figured out how to adopt chrono to work but I tried setting everything having to do with time to zero.
The second problem, after copying code from Environment.h to make something that's equivalent to those addListBox(), addStaticText() and such functions, I can't get my new element to show up. Maybe it has to do with defining out all the timing code that can't work. Maybe it's from something else. Here's what I'm using to manually create my new ui object.
This CGUITTFont class is really good. I can generate a bunch of different fonts and then simply use them with SetOverrideFont(). I set things up so I have a database object full of every even number font size between 10 and 60, then I use ui resizing code to select the correct font based on screen size.
One thing I haven't figured out yet is how do I set the override text of a IGUIListBox? It doesn't have a SetOverrideFont member function and every method i've tried of getting the listbox's children so I can apply SetOverrideFont to them have not worked out.
For example this doesn't work. All it picks up is 1 IGUIScrollBar and no text labels.
Code: Select all
int ctr = 0;
core::list<IGUIElement*>::ConstIterator it;
for (it=m_consoleHistory->getChildren().begin(); it!=m_consoleHistory->getChildren().end();++it)
{
ctr++;
cout << "it type = " << (*it)->getType() << endl;
if ((*it)->hasType(EGUIET_STATIC_TEXT))
{
IGUIStaticText *statTextPtr = (IGUIStaticText*)(*it);
statTextPtr->setOverrideFont(sansttfFonts.getSizedFont(20*res.Height/480));
}
}
Where is the content of the IGUIListBox class anyway? The header file doesn't contain enough code and there isn't a IGUIListBox.cpp file anywhere. I tried making my own IGUIListBox class from IGUIListBox.h and CGUIListBox.cpp but now there's 2 problems I can't figure out.
First of all, I can't find any reference to "os.h", I can't find it on my system and I can't find anything relevant about it on the internet. I am able to compile irrlicht for some reason but I don't have os.h or <os> or whatever it's supposed to be called. I haven't figured out how to adopt chrono to work but I tried setting everything having to do with time to zero.
The second problem, after copying code from Environment.h to make something that's equivalent to those addListBox(), addStaticText() and such functions, I can't get my new element to show up. Maybe it has to do with defining out all the timing code that can't work. Maybe it's from something else. Here's what I'm using to manually create my new ui object.
Code: Select all
m_consoleHistory = new IGUIFontListBox(irrsmgr->getGUIEnvironment(), 0, GE_LISTBOX_CONSOLE, core::rect<s32>(0,0,640,480), true, true, false);
IGUISkin *CurrentSkin = irrsmgr->getGUIEnvironment()->getSkin();
if (CurrentSkin && CurrentSkin->getSpriteBank())
{
m_consoleHistory->setSpriteBank(CurrentSkin->getSpriteBank());
}
else if (irrsmgr->getGUIEnvironment()->getBuiltInFont() && irrsmgr->getGUIEnvironment()->getBuiltInFont()->getType() == EGFT_BITMAP)
{
m_consoleHistory->setSpriteBank( ((IGUIFontBitmap*)irrsmgr->getGUIEnvironment()->getBuiltInFont())->getSpriteBank());
}
Re: How do I either resize static text OR draw static text to an ITexture?
The interfaces in Irrlicht usually start with 'I' like IListBox and are in the header folder. The implementation are then in source/Irrlicht folder and start with 'C' like CListBox. So it's in files CListBox.h/.cpp. And it looks like indeed the listbox isn't having the setOverrideFont function yet. Seems I haven't noticed yet because my listboxes all use the global font so replacing that one was always sufficient :-)
I may have some time later to code it (should be trivial I think)
I may have some time later to code it (should be trivial I think)
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Re: How do I either resize static text OR draw static text to an ITexture?
OK, update svn trunk and IGUIListBox has the function now :)
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm