-
Hey guys,
I am stuck with certain problem: I manage stuff inside of sth you can call a scene and I want to keep the aspect ratio of the scene when the window is resized. I want to do this by changing the viewport. I am used to the fact that the viewport is given in pixels as sf::View seems to use percentage values of the window size I scale my calculated viewport down with these lines:
// down scale to % units ; windoww/h are from the resize event
viewport.width = viewport.width / windoww;
viewport.height = viewport.height / windowh;
// center
viewport.left = ( 1.0 - viewport.width ) * 0.5;
viewport.top = ( 1.0 - viewport.height ) * 0.5;
But it seems that my calculations are always wrong :/ and I guess I am stuck in my head so I can't see the problem. I tried this way to get the right upscaled/cropped size:
// in = ( scenew, sceneh ); clip = ( windoww, windowh )
sf::Vector2f scaleToFit( const sf::Vector2f& in, const sf::Vector2f& clip )
{
sf::Vector2f ret( in );
if ( ( clip.y * in.x ) / in.y >= clip.x )
{
ret.y = ( clip.x * in.y ) / in.x;
ret.x = clip.x;
}
else if ( ( clip.x * in.y ) / in.x >= clip.y )
{
ret.x = ( clip.y * in.x ) / in.y;
ret.y = clip.y;
}
else
ret = clip;
return ret;
}
All I get is strange resizing. The scene keeps in the center but if I extend the width of the window (there should be black panels on the left and right) the black panels get very big so the scene does not keep its ratio and I don't know why. I hope I can get some help here.
Just for your information: This is what all happens on the resize event but I don't expect an error there:
sf::View v( sf::FloatRect( 0, 0, scenew, sceneh ) );
sf::FloatRect viewport( sf::Vector2f( 0, 0 ), scaleToFit( sf::Vector2f( screenw, screenh ), sf::Vector2f( windoww, windowh ) ) );
viewport.width = viewport.width / basicSettings.windoww;
viewport.height = viewport.height / basicSettings.windowh;
viewport.left = ( 1.0 - viewport.width ) * 0.5;
viewport.top = ( 1.0 - viewport.height ) * 0.5;
v.setViewport( viewport );
window.setView( v );
-
You aim seems to be keeping the view constantly letterboxed. Does the view really have to be resized with the window? Can't you just adjust the aspect of it and keep one of the sides always matching the original view?
Anyway, it looks like
sf::FloatRect viewport( sf::Vector2f( 0, 0 ), scaleToFit( sf::Vector2f( screenw, screenh ), sf::Vector2f( windoww, windowh ) ) );
should be
sf::FloatRect viewport( sf::Vector2f( 0, 0 ), scaleToFit( sf::Vector2f( scenew, sceneh ), sf::Vector2f( windoww, windowh ) ) );
(scenew & sceneh instead of screenw & screenh)
-
Yeah that is just a typo. As I compared it to a scene I renamed the variables in the code (except these two). So this is not a real "error".
Letterboxed ... that was the word ... Well at the right window ratio there are no letterboxes(obvious) but yes I want the letterboxes.
-
In this line:
viewport.width = viewport.width / windoww;
you are dividing the current ratio by the size of the window in pixels and then assigning that as the new ratio.
I think it should rather be:
viewport.width = currentViewportWidthInPixels / windoww;
Obviously, the same sort of thing would also apply to the height line.
-
As scenew, sceneh, windoww and windowh contain values in pixel unit scaleToFit returns unit pixel. So viewport.width/.height contain pixel values.
-
Ah, I see; that makes sense.
However, the .setViewPort requires ratios, not pixels.
Also, your viewport.left and viewport.top are based on viewport.width and viewport.height being ratios.
-
That is why I scale the size values by dividing by their maximum pixel units (window size). The position is just position in the center.
-
My point is that you're scaling using one ratio and one pixel length to get a ratio. They should be both ratios or both pixel lengths.
e.g.
viewport.width = 0.5
windoww = 600
then
viewport.width = 0.5 / 600 = 0.000833
0.000833 is probably not the width you wanted.
-
I got your point, but the units are fine(and they were the right units all the time). I was testing an other way, but here is the same issue that it is scaling weird. I looks like this generates no panels when the window is like a square and scales wrong if it is rectangular.
// not edited
sf::View v( sf::FloatRect( sf::Vector2f( basicSettings.screenx, basicSettings.screeny ), \
sf::Vector2f( basicSettings.screenw, basicSettings.screenh ) ) );
sf::Vector2f S( basicSettings.screenw, basicSettings.screenh );
sf::Vector2f W( basicSettings.windoww, basicSettings.windowh );
float scalor( std::min( W.x / S.x, W.y / S.y ) ); // squared window preference?!
sf::Vector2f scaled( S );
scaled *= scalor;
printf( "scaled: %f %f\n", scaled.x, scaled.y );
sf::FloatRect viewport( sf::Vector2f( 0, 0 ), scaled );
viewport.width = viewport.width / W.x;
viewport.height = viewport.height / W.y;
viewport.left = ( 1.0 - viewport.width ) * 0.5;
viewport.top = ( 1.0 - viewport.height ) * 0.5;
v.setViewport( viewport ); // in the end still not the same ratio as the scene
basicSettings.window.setView( v );
Why did anyone chose to set the viewport in percent units? Why not in pixels as the glViewport does? It was easier with pixel values to check the problem :/ as you always had to deal with pixels when changing the viewport.
-
Why did anyone chose to set the viewport in percent units? Why not in pixels as the glViewport does? It was easier with pixel values to check the problem :/ as you always had to deal with pixels when changing the viewport.
There are no percentages; they're ratios. This question is answered in the tutorial (http://www.sfml-dev.org/tutorials/2.1/graphics-view.php#defining-how-the-view-is-viewed):
the viewport is not defined in pixels, but rather as a ratio of the window size. This is much more convenient: this way you don't have to track resize events to update the size of the viewport. It is also more intuitive: you will most likely need to define your viewport as a fraction of your window, not as a fixed-size rectangle.
I got your point, but the units are fine(and they were the right units all the time).
I can't see a possible way that the units could have been fine. Either you were using a ratio where a pixel size should have been used or you were using a pixel size where a ratio should have been used.
I must admit that I had a tiny bit of trouble with keeping aspect ratio of view constant when window's aspect ratio changed, but it turned out to be not that difficult (I'm slow :P); keep at it! ;)
-
Well percentages and ratios both are usually used with values between 0..1 and as I am not a native English speaker I might tend to use wrong words.
Ok, I got the reason why ratios are used. Why do you stick to the thought that the units were/are wrong and not the values? Check the code, I convert from pixel to ratio with this:
viewport.width = viewport.width / W.x;
viewport.height = viewport.height / W.y;
And all before this is pixel and all after this is in ratio units.
Can't you write something more helpful? If you already had the same problem why can't you give a hint, where I should rethink my code or give (pseudo-)code.
-
Something like this?
float screenwidth = windowsize.x / static_cast<float>(gamesize.x);
float screenheight = windowsize.y / static_cast<float>(gamesize.y);
sf::FloatRect viewport;
viewport.width = 1.f;
viewport.height = 1.f;
if(screenwidth > screenheight)
{
viewport.width = screenheight / screenwidth;
viewport.left = (1.f - viewport.width) / 2.f;
}
else if(screenwidth < screenheight)
{
viewport.height = screenwidth / screenheight;
viewport.top = (1.f - viewport.height) / 2.f;
}
sf::View v( sf::FloatRect( 0, 0, gamesize.x , gamesize.y ) );
v.setViewport(viewport);
-
I tried yours maly and it ends up the same as my implementation.
float screenwidth = static_cast<float>(basicSettings.windoww) / static_cast<float>(basicSettings.screenw);
float screenheight = static_cast<float>(basicSettings.windowh) / static_cast<float>(basicSettings.screenh);
sf::FloatRect viewport;
viewport.width = 1.f;
viewport.height = 1.f;
if( screenwidth > screenheight )
{
viewport.width = screenheight / screenwidth;
viewport.left = (1.f - viewport.width) / 2.f;
}
else if(screenwidth < screenheight)
{
viewport.height = screenwidth / screenheight;
viewport.top = (1.f - viewport.height) / 2.f;
}
sf::View v( sf::FloatRect( sf::Vector2f( basicSettings.screenx, basicSettings.screeny ), \
sf::Vector2f( basicSettings.screenw, basicSettings.screenh ) ) );
v.setViewport(viewport);
basicSettings.window.setView( v );
I guess I make some screenshots:
Album: http://imgur.com/a/IigTy
On creation
(http://i.imgur.com/D9oOOZd.png) (http://imgur.com/D9oOOZd)
Resize on x-axis
(http://i.imgur.com/uKjcffT.png) (http://imgur.com/uKjcffT)
Then resize on y-axis
(http://i.imgur.com/D9oOOZd.png) (http://imgur.com/D9oOOZd)
-
just put something together rather quick, wonder if it's the same kind of thing your trying to do.
void Game::MaintainAspectRatio()
{
//first we check our new aspect width to see if it changed
float newAspectWidth = window.getSize().x;
float newAspectHeight = window.getSize().y;
if(newAspectWidth != currentAspectWidth)
{
//width changed, maintain the aspect ratio and adjust the height
currentAspectWidth = newAspectWidth;
currentAspectHeight = currentAspectWidth / aspectRatio;
}
else if(newAspectHeight != currentAspectHeight)
{
//height changed, maintain aspect ratio and change the width
currentAspectHeight = newAspectHeight;
currentAspectWidth = currentAspectHeight * aspectRatio;
}
std::cout << "width: " << currentAspectWidth << " height: " << currentAspectHeight;
window.setSize(sf::Vector2u(currentAspectWidth, currentAspectHeight));
}
Where aspectRatio is always 4:3.
Edit: I should also mention that this gets called whenever theres a Resize Event.
-
I get your dirty trick behind it as you limit the window size. I don't really like it as I cannot freely do with the window what I want (as a player).
I tried your code with currentAspectWidth and currentAspectHeight as floats. After resizing once the window shrunk:
width: 480.000031 height: 432.000000
width: 468.000000 height: 421.199982
width: 421.000000 height: 378.899994
width: 378.000000 height: 340.199982
width: 340.000000 height: 306.000000
width: 306.000000 height: 275.399994
width: 275.000000 height: 247.499985
width: 247.000000 height: 222.299988
width: 222.000000 height: 199.799988
width: 199.000000 height: 179.099991
width: 179.000000 height: 161.099991
width: 161.000000 height: 144.899994
width: 144.000000 height: 129.599991
width: 129.000000 height: 116.099991
width: 116.000000 height: 104.399994
width: 104.000000 height: 93.599998
width: 93.000000 height: 83.699997
width: 83.000000 height: 74.699997
width: 74.000000 height: 66.599998
width: 66.000000 height: 59.399998
width: 59.000000 height: 53.099998
width: 53.000000 height: 47.699997
width: 47.000000 height: 42.299999
width: 42.000000 height: 37.799999
width: 37.000000 height: 33.299999
width: 33.000000 height: 29.699999
width: 29.000000 height: 26.099998
width: 26.000000 height: 23.400000
width: 23.000000 height: 20.699999
width: 20.000000 height: 18.000000
width: 18.000000 height: 16.199999
width: 16.000000 height: 14.400000
width: 14.000000 height: 12.599999
width: 12.000000 height: 10.799999
width: 10.000000 height: 9.000000
width: 9.000000 height: 8.099999
width: 8.000000 height: 7.200000
width: 7.000000 height: 6.300000
width: 6.000000 height: 5.400000
width: 5.000000 height: 4.500000
width: 4.000000 height: 3.600000
width: 3.000000 height: 2.700000
width: 2.000000 height: 1.800000
width: 1.000000 height: 0.900000
width: 0.000000 height: 0.000000
I also tried to change the floats to ints(just currentAspect***) and the same phenomenom:
width: 480 height: 432
width: 533 height: 479
width: 479 height: 431
width: 431 height: 387
width: 387 height: 348
width: 348 height: 313
width: 313 height: 281
width: 281 height: 252
width: 252 height: 226
width: 226 height: 203
width: 203 height: 182
width: 182 height: 163
width: 163 height: 146
width: 146 height: 131
width: 131 height: 117
width: 117 height: 105
width: 105 height: 94
width: 94 height: 84
width: 84 height: 75
width: 75 height: 67
width: 67 height: 60
width: 60 height: 53
width: 53 height: 47
width: 47 height: 42
width: 42 height: 37
width: 37 height: 33
width: 33 height: 29
width: 29 height: 26
width: 26 height: 23
width: 23 height: 20
width: 20 height: 17
width: 17 height: 15
width: 15 height: 13
width: 13 height: 11
width: 11 height: 9
width: 9 height: 8
width: 8 height: 7
width: 7 height: 6
width: 6 height: 5
width: 5 height: 4
width: 4 height: 3
width: 3 height: 2
width: 2 height: 1
width: 1 height: 0
width: 0 height: 0
-
I must've misread your question, but what this does for me is as I alter one dimension of the screen, the other dimension adjusts to maintain a 4:3 aspect Ratio. Coupled with a sf::Style::Resize in the window, this allows me to drag the window to whatever size I'm most comfortable with. What exactly are you looking for?
-
I want to always see the whole scene so if my scene has an aspect ratio of 1:1 and the width is greater than the height of the window I need black panels on the upper and lower part of the window (vice versa for height > width). And I try to achieve this with using glViewport as used with the viewport from sf::View.
-
when you resize the window always set a new view.
basicSettings.screenx and basicSettings.screeny doing strange offsets.
sf::View v( sf::FloatRect( sf::Vector2f( basicSettings.screenx, basicSettings.screeny ), \
sf::Vector2f( basicSettings.screenw, basicSettings.screenh ) ) );
try
sf::View v( sf::FloatRect( sf::Vector2f( 0, 0 ), \
sf::Vector2f( basicSettings.screenw, basicSettings.screenh ) ) );
example
#include <SFML/Graphics.hpp>
sf::View calcView(const sf::Vector2u &windowsize, const sf::Vector2u &designedsize)
{
sf::FloatRect viewport(0.f, 0.f, 1.f, 1.f);
float screenwidth = windowsize.x / static_cast<float>(designedsize.x);
float screenheight = windowsize.y / static_cast<float>(designedsize.y);
if(screenwidth > screenheight)
{
viewport.width = screenheight / screenwidth;
viewport.left = (1.f - viewport.width) / 2.f;
}
else if(screenwidth < screenheight)
{
viewport.height = screenwidth / screenheight;
viewport.top = (1.f - viewport.height) / 2.f;
}
sf::View view( sf::FloatRect( 0, 0, designedsize.x , designedsize.y ) );
view.setViewport(viewport);
return view;
}
int main()
{
const sf::Vector2u designedsize(320,200);
sf::RenderWindow app(sf::VideoMode(800, 400), "SFML window");
app.setView(calcView(app.getSize(), designedsize));
while (app.isOpen())
{
sf::Event event;
while (app.pollEvent(event))
{
if (event.type == sf::Event::Closed)
app.close();
if (event.type == sf::Event::Resized)
app.setView(calcView(sf::Vector2u(event.size.width, event.size.height), designedsize));
}
app.clear();
sf::CircleShape circle(5);
for(int y = 0; y < designedsize.y/10; ++y)
{
for(int x = 0; x < designedsize.x/10; ++x)
{
circle.setPosition(x * 10, y * 10);
circle.setFillColor( (x + y) & 1 ? sf::Color::Blue:sf::Color::Green);
app.draw(circle);
}
}
app.display();
}
return 0;
}
-
Here is how I would do it.
I hope it helps.
sf::View calcView(const sf::Vector2f& windowSize, float minRatio, float maxRatio)
{
sf::Vector2f viewSize = windowSize;
// clip ratio
float ratio = viewSize.x / viewSize.y;
if(ratio < minRatio) // too high
viewSize.y = viewSize.x / minRatio;
else if(ratio > maxRatio) // too wide
viewSize.x = viewSize.y * maxRatio;
sf::View view(sf::FloatRect(sf::Vector2f(), viewSize));
sf::FloatRect viewport((windowSize - viewSize) / 2.f, viewSize);
viewport.left /= windowSize.x;
viewport.top /= windowSize.y;
viewport.width /= windowSize.x;
viewport.height /= windowSize.y;
view.setViewport(viewport);
return view;
}
-
Well percentages and ratios both are usually used with values between 0..1 and as I am not a native English speaker I might tend to use wrong words.
For your information, percentage is usually in the range of 0 to 100.
I convert from pixel to ratio with this:
viewport.width = viewport.width / W.x;
viewport.height = viewport.height / W.y;
And all before this is pixel and all after this is in ratio units.
You store pixel width and ratio width in the same variable - viewport.width? I see that now but it's not very clear or logical.
Can't you write something more helpful? If you already had the same problem why can't you give a hint, where I should rethink my code or give (pseudo-)code.
A lot of code has been given by others since this but I'm still not 100 percent sure what your aim is.
I know that you want letterboxing when the window is no longer the same ratio.
I don't think you've specified whether or not you want the actual letterboxed display to stay the original size or stretch to fit the window. If stretched, the view can stay almost the same (which was why I mentioned it in my first post). If not, the view is the identical to the window and the viewport is the only thing that changes.
If you still haven't solved it from others' code, and you've explained what your aim is, let me know and I'll look into it for you (it's been a while since I worked with this so I don't have the answer at hand).
-
@Hapax: Well then I know why we miscommunicate as you see percentage in a range of 0..100(which is absolutely right) and I just called it percentage as it should not be larger than 100% = 1.0.
I can understand your misinterpretation of the doubled usage of the floatrects properties width/height on the one hand in pixels and on the one hand in ratios.
The code from the others works, but I found an other error(continue reading).
@mely: I just tested your example and it worked perfectly. I put it into my application and it was working. Then I changed the code from relying on window.getSize( ) on my variables windoww and windowh. Then it was not working and stretching wrong. Then I found a typo in my resize-event where I asign the new width and height to windoww but windowh is not affected. So I guess even my code should have worked without the typo (not tested).
Thank you for your patience and pointing me (unintentionally) to an error (I should have been aware of).