Improving The Behavior or HorizSizing/VertSizing == "relative
by Edward F. Maurina III · in Torque Game Engine · 04/07/2006 (5:09 am) · 10 replies
@Community
I recently fielded a question from Matthew Johnson. He was experiencing an issue where his controls would migrate over time as he editted.
Although I was aware of this behavior, it didn't occur to me right away that he was seeing a little-discussed issue with 'relative' resizing.
In TGE, relative resizing affects both the position and size of a child control. That is, when a parent control is resized, all of its children are resized also. Furthermore, a control using relative resizing will re-scale and reposition based on the re-scale factor. For example, if you are viewing in 1024x768 and start your GUI editor in 800x600, the multiplying factor is 0.78125. Thus, all relatively sized controls are translated and scaled by this factor.
This might not seem to be a problem until one remembers that all control sizes/positions are in pixels which are measured as integers. However, the scaling results in a floating-point value. Herein lies the problem. The current algorithm for scaling of controls does not account for floating-point to integer truncation.
If you will recall, when a floating-point value is stuffed into an intger variable, the mantissa is simply truncated, not rounded. Thus whenever a value has a mantissa > 0.5 we effectively lose one pixel.
So, before I turn this into a lecture, let me just cut to the chase.
This problem has bothered me for some time, but I didn't do anything abou it. However, with Matthew raising the issue again, I decided to fix it.
So, if you are currently working with TGE 1.4 you can edit the file guiControl.cc and modify the code starting at about line 340 to look like this:
Yes, this code is a bit bruteforce-ish, but it should be clear what is being done.
With this code change, and using an editting resoultion of 800x600 with a previewing resoultion of 1024x768, you will get zero truncation errors.
Note: This does not solve migration from non-adjacent resolution scalings, but its a definite improvement.
Cheers!
Hall Of Worlds - For Gamers
EdM|GPGT
I recently fielded a question from Matthew Johnson. He was experiencing an issue where his controls would migrate over time as he editted.
Although I was aware of this behavior, it didn't occur to me right away that he was seeing a little-discussed issue with 'relative' resizing.
In TGE, relative resizing affects both the position and size of a child control. That is, when a parent control is resized, all of its children are resized also. Furthermore, a control using relative resizing will re-scale and reposition based on the re-scale factor. For example, if you are viewing in 1024x768 and start your GUI editor in 800x600, the multiplying factor is 0.78125. Thus, all relatively sized controls are translated and scaled by this factor.
This might not seem to be a problem until one remembers that all control sizes/positions are in pixels which are measured as integers. However, the scaling results in a floating-point value. Herein lies the problem. The current algorithm for scaling of controls does not account for floating-point to integer truncation.
If you will recall, when a floating-point value is stuffed into an intger variable, the mantissa is simply truncated, not rounded. Thus whenever a value has a mantissa > 0.5 we effectively lose one pixel.
So, before I turn this into a lecture, let me just cut to the chase.
This problem has bothered me for some time, but I didn't do anything abou it. However, with Matthew raising the issue again, I decided to fix it.
So, if you are currently working with TGE 1.4 you can edit the file guiControl.cc and modify the code starting at about line 340 to look like this:
else if (mHorizSizing == horizResizeRelative && oldParentExtent.x != 0)
{
F32 oldParentExtentX = oldParentExtent.x;
F32 newParentExtentX = newParentExtent.x;
F32 scale = newParentExtentX / oldParentExtentX;
F32 oldPosX = newPosition.x;
F32 newPosX = (oldPosX * scale);
F32 oldExtentX = newExtent.x;
F32 newExtentX = (oldExtentX * scale);
if( (newPosX - mFloor(newPosX)) < 0.5 )
newPosX = mFloor(newPosX);
else
newPosX = mFloor(newPosX) + 1.0;
if( (newExtentX - mFloor(newExtentX)) < 0.5 )
newExtentX = mFloor(newExtentX);
else
newExtentX = mFloor(newExtentX) + 1.0;
newPosition.x = newPosX;
newExtent.x = newExtentX;
}
if (mVertSizing == vertResizeCenter)
newPosition.y = (newParentExtent.y - mBounds.extent.y) >> 1;
else if (mVertSizing == vertResizeHeight)
newExtent.y += deltaY;
else if (mVertSizing == vertResizeTop)
newPosition.y += deltaY;
else if(mVertSizing == vertResizeRelative && oldParentExtent.y != 0)
{
F32 oldParentExtentY = oldParentExtent.y;
F32 newParentExtentY = newParentExtent.y;
F32 scale = newParentExtentY / oldParentExtentY;
F32 oldPosY = newPosition.y;
F32 newPosY = (oldPosY * scale);
F32 oldExtentY = newExtent.y;
F32 newExtentY = (oldExtentY * scale);
if( (newPosY - mFloor(newPosY)) < 0.5 )
newPosY = mFloor(newPosY);
else
newPosY = mFloor(newPosY) + 1.0;
if( (newExtentY - mFloor(newExtentY)) < 0.5 )
newExtentY = mFloor(newExtentY);
else
newExtentY = mFloor(newExtentY) + 1.0;
newPosition.y = newPosY;
newExtent.y = newExtentY;
}Yes, this code is a bit bruteforce-ish, but it should be clear what is being done.
With this code change, and using an editting resoultion of 800x600 with a previewing resoultion of 1024x768, you will get zero truncation errors.
Note: This does not solve migration from non-adjacent resolution scalings, but its a definite improvement.
Cheers!
Hall Of Worlds - For GamersEdM|GPGT
About the author
Recent Threads
#2
Edit: after moving around my GUI like crazy the children are actually getting thrown off a bit, but it adds up. I'm going to try to write a seamless version, but I'm not sure if I can write anything more seamless than this, we'll see how it all works out.
07/15/2006 (1:52 pm)
Thanks! It is a rather brute force way to do it as you pointed out, but I had a GUI control that was being resized every render and its children were getting thrown all over the place. Maybe I'll try to write a faster way to do it later but for now this will work.Edit: after moving around my GUI like crazy the children are actually getting thrown off a bit, but it adds up. I'm going to try to write a seamless version, but I'm not sure if I can write anything more seamless than this, we'll see how it all works out.
#3
If your goal is to round off a float to an integer then the following code is a little simpler:
07/15/2006 (11:06 pm)
if( (newExtentY - mFloor(newExtentY)) < 0.5 )
newExtentY = mFloor(newExtentY);
else
newExtentY = mFloor(newExtentY) + 1.0;If your goal is to round off a float to an integer then the following code is a little simpler:
newExtentY = mFloor(newExtentY+0.5);
#4
Because of this, I've recently decided to re-write a lot of the gui system for Torque. The way I'm handling the layout is simply by having fixed html-style measurement values (eg. "4 px", "5.5%", "3pt", "*", or whatever) separate to the actual on-screen values.
I'd suggest implementing something similar, but... unfortunately, it wouldn't be a trivial matter to integrate it into the existing gui control codebase since so many of the classes just ignore good coding/encapsulation practices. If you're up for the challenge though...
07/16/2006 (11:16 am)
I can't say I've ever been a fan of the Torque gui system. I like the interaction with scripting, but the layout system is just plain bad (not to mention counter-intuitive to the artists I work with who always think that aligning a control left should, well, align it to the left -- not to the right).Because of this, I've recently decided to re-write a lot of the gui system for Torque. The way I'm handling the layout is simply by having fixed html-style measurement values (eg. "4 px", "5.5%", "3pt", "*", or whatever) separate to the actual on-screen values.
I'd suggest implementing something similar, but... unfortunately, it wouldn't be a trivial matter to integrate it into the existing gui control codebase since so many of the classes just ignore good coding/encapsulation practices. If you're up for the challenge though...
#5
07/17/2006 (1:56 pm)
I think my problem is that those few decimals that are getting cut off are making a difference, I am going to write what will hopefully be a small bit of code to store the whole float so we can use that and hopefully not get thrown off.
#6
07/17/2006 (8:54 pm)
Sorry, but... what's the point in using integer coordinates? Let's just change GuiControl to use floating point everywhere. I can't see what problems it may cause and make more sense given that OpenGL uses floating point even for 2D rendering anyway.
#7
I wrote some code to enable any control to smoothy and gradually change size & position,
and it definitely relies on the addition of some floating-point representations of the coords which are what do the real work and are only converted back to integers at the very end.
07/18/2006 (12:54 am)
I think that's a capital idea.I wrote some code to enable any control to smoothy and gradually change size & position,
and it definitely relies on the addition of some floating-point representations of the coords which are what do the real work and are only converted back to integers at the very end.
#8
07/18/2006 (5:06 am)
Yes I was planning to do what Orion has done I am having trouble finding time to do it because I have and will be quite busy but it will get done.
#9
this will fix it quite elegantly.
07/19/2006 (12:08 pm)
Http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=2224this will fix it quite elegantly.
#10
07/19/2006 (7:20 pm)
Hmmm, 2002... broken in version 1.1.1... If people at GG haven't fixed this, even they must have been very busy :(
Associate James Urquhart
That way, it wouldn't matter if you changed screen resolution - all controls would move in the defined coordinate system, rather than attempting to use the screen's coordinate system.
Perhaps one could apply this concept to nested controls too - i.e the parent control would define the coordinate system, which the children would conform to whatever the real size of the parent control is.
Just an idea, anyway :)