SDL2 OpenGL window shrinking and changing mouse coordinates - c++

After shrinking my applications to window size mouse coordinates won't change. Graphics work fine, just mouse coordinates are problem.
My applications native size is 1920x1080. But I wan't to shrink app to 1280x720.
Here is what I'm doing right now:
Create window with 1280x720 w/h
appWindow = SDL_CreateWindow(GAME_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOWPOS_CENTERED);
I set glViewport to match with window
glViewport(0,0,1280,720);
I make set glOrtho with native size 1920x1080
glOrtho(0, 1920, 1080, 0, -1, 1);
Like I already said, by doing this graphics do scale on new window size but mouse coordinates don't change and i need to know how to change them.

I am not sure i understand your question, but i think here is problem: glOrtho() is opengl comand not sdl, so it affects opengl graphics only. Mouse and windowing is controlled by SDL in your case.
I dont understand what you are trying to do by saying "My applications native size is 1920x1080. But I wan't to shrink app to 1280x720." if you want to resize window use SDL_SetWindowSize. And then adjust opengl viewport and projection with glViewport and glOrtho, so your opengl view and sdl window size is in sync.
Edit 1: If you want to render as it is now, by using larger view than windows, then you need to calculate mouse position by your self. sdl gives you mouse relative to window size not opengl view and projection. Math should be quite simple if you are just scaling the view.
Edit 2: in your case glOrtho(0, 1920, 1080, 0, -1, 1) projects 1920x1080 image to glViewport(0,0, 1280 ,720) view. So it scales down if i am not mistaken. So you need to scale mouse pos down to relative to scale ratio:
untested code:
mouseX= 1920.0/1280.0 * winMouse.x
mouseY= 1080.0/720.0 * winMouse.y;
I ussually experiment alot, so just try to understand what opengl does and experiment.

Related

Alpha Blending in SDL resets after resizing window

I wanted to implement alpha blending within my Texture class. It works almost completely. I use the following functions for manipulating the alpha value:
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, alpha);
The only problem I have is that the textures that have been manipulated seem to reset to the normal alpha value of 255 when I resize or maximize the window. I checked the alpha value and recognized that it is still the value I manipulated it to be before. So the value is not 255. Why is the renderer rendering it as if the alpha value was 255 then?
Information about how and when I use these functions:
Within the main game loop I change the alpha value of the texture with a public method of my Texture class:
Texture::setAlphaValue(int alpha)
There the private alpha variable of the Texture class is changed.
Within the Draw method of my Texture class the texture is drawn and I call
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(texture, alpha);
before
SDL_RenderCopyEx(renderer, texture, &sourceRectangle, &destinationRectangle, 0, 0, SDL_Flip);
Information about how I resize the window:
I basically just set the window mode to a resizable window in my SDL initialization. Then handling it like any normal window is possible:
SDL_CreateWindow(window_Title, x_Position, y_Position, window_Width, window_Height, SDL_WINDOW_RESIZABLE);
My primary loop area:
This is the main game loop:
void Game::Render()
{
// set color and draw window
SDL_SetRenderDrawColor(renderer, windowColor.R(), windowColor.G(), windowColor.B(), 0);
SDL_RenderClear(renderer);
texture.setAlphaValue(100);
texture.Draw(SDL_FLIP_NONE);
// present/draw renderer
SDL_RenderPresent(renderer);
}
Test my project:
I also uploaded my alpha-blending test project to dropbox. In this project I simplified everything, there isn't even a texture class anymore. So the code is really simple, but the bug is still there. Here is the link to the Visual Studio project: http://www.dropbox.com/s/zaipm8751n71cq7/Alpha.rar
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
You should directly change the alpha in this area.
example: alpha = 100;
SDL_SetTextureAlphaMod(texture, alpha); //remember that alpha is an int
SDL_RenderCopy(renderer, texture, NULL, &rect);
P.S. If you're going for a fade-out/fade-in effect, resizing will temporarily pausa alpha changes (in-case you used SDL_GetTicks() and made a float to slowly reduce/increase alpha as time goes by. This is because windows pauses the rendering inside the program but once you stop resizing, it resumes.
Another P.S. Since you're resizing the window make sure to assign the w and h values not as numbers but as products or dynamic numbers(Multiplication is faster than division but you can also use division).
Assigning static numbers would cause the window to resize but the textures inside won't change size.
Happy Coding :)
This has been a reported bug in the SDL library. It is fixed for some time now: https://bugzilla.libsdl.org/show_bug.cgi?id=2202, https://github.com/libsdl-org/SDL/issues/1085

SDL2 resize buggy in Fedora 21 but fine in windows

I'm building a cross platform C++ OpenGL game using SDL2 as the window manager, and I recently discovered that resizing the window has some strange issues in linux. Other windows like Terminal work fine for resizing. It may also be worth noting that Fedora is in a VMware Virtual Machine.
In windows resizing functions as normal.
In Fedora21 resizing acts weird. The best I can explain it is, while resizing the window:
it always stretches out the right side when the mouse is stationary
it sometimes resizes vertically to the top of the screen despite only resizing the sides
Resizing inwards from the bottom works until the mouse is stationary and then the window starts stretching back outwards at a constant rate (while also stretching outwards to the right)
Only sometimes does the window snap to where you left your mouse when you release it. Other times it stays at the self-stretched position. Depends on which side you're stretching from.
When the window doesnt snap back to where you released it, sometimes the window moves to the correct width, but located from where the window stretched right to.
I'm aware that it may be something in my code, so I've provided the relevant code below. Let me know if you need to see more. Its just weird that it happens on one OS but not another. I've also compiled on OSX, so I'll see what happens on there as well.
Relevant event cases
case SDL_WINDOWEVENT_RESIZED:
if (event.window.windowID == SDL_GetWindowID(mainWindow))
{
SDL_SetWindowSize(mainWindow, event.window.data1, event.window.data2);
reshape(event.window.data1, event.window.data2);
postRedisplay(); // Change state to redraw
}
break;
And my reshape code
void reshape(int w, int h)
{
g.width = w;
g.height = h;
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, 1, -1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Thanks for the help. The problem was that SDL was handling the window resizing, but then I was also manually handling it. I don't know why it wasn't an issue in windows, I guess it is handled differently in windows.
Solution: remove the following line
SDL_SetWindowSize(window, event.window.data1, event.window.data2);

OpenGL with MFC

I am trying to make 4 OpenGL viewports inside a CSplitterWnd, but am having some problems.
At first, I had flickering and drawing issues until I added the flag PFD_SUPPORT_GDI into the pixel format, which made everything work nicely together. But when I use PFD_SUPPORT_GDI, I am only able to get a 1.1 OpenGL context.
Is it possible to use PFD_SUPPORT_GDI with a version of OpenGL higher than 1.1 so that I can use VBOs? or is there another way to get OpenGL to work properly without PFD_SUPPORT_GDI?
The biggest problem with not having PFD_SUPPORT_GDI is that the splitter window separator wipes the viewport contents away when you drag over it..which does not happen while using the PFD_SUPPORT_GDI flag.
PFD_SUPPORT_GDI means, you want to be able to draw using GDI calls, which will force you into using the software renderer.
Most of the time flicker issues, especially with MFC are due to not properly set/choosen WNDCLASS(EX) parameters. Most importantly CS_OWNDC flag should be set and the background brush should be NULL. Also you should overwrite the OnEraseBackground handler and implement a OnPaint handler, that reports a validated rect.
PFD_SUPPORT_GDI means that you can do GDI drawing to the window. This forces a software OpenGL implementation, because you cannot use GDI drawing (which is software) with hardware-based OpenGL drawing.
So no, you cannot have both hardware OpenGL (or D3D) acceleration and GDI support for the same window. If you're having issues with what happens to the contents of such windows, that is something you should resolve in some other way. Perhaps you could simply redraw the view when its size is changed or something.
I decided the best way to do this was to use a frame buffer. Handling OnEraseBackground() helped with the flicker, but the MFC still just doesn't want to play nice with OpenGL, so I had to go with a GDI solution.
Each viewport first gets drawn to it's own frame buffer, and then blitted to the appropriate window.
void FrameBuffer::Blit(HDC hDC, int width, int height)
{
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, blitBuffer);
SetDIBitsToDevice(hDC, 0, 0, width, height, 0, 0, 0, height, blitBuffer, &blitInfo, DIB_RGB_COLORS);
}
This solution doesn't seem to be making any visible impact on performance.

Painting Text above OpenGL context in MFC

I work on an MFC app containing OpenGL context.I am new to MFC that is why I am asking it.OpenGL works fine ,but when I want to draw a text above the 3D window using this code inside WindowProc:
case WM_PAINT:
hDC=BeginPaint(window,&paintStr);
GetClientRect(window,&aRect);
SetBkMode(hDC,TRANSPARENT);
DrawText(hDC,L"He He I am a text on top of OpenGL",-1,&aRect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(window,&paintStr);
return 0;
it is shown beneath the OpenGL context.I can see it only when resizing the window as the OpenGL rendering pauses than.
What you're doing is wrong and also harder than doing it all in OpenGL. To solve the problem of adding text to an OpenGL-drawn window, it's better to just make OpenGL draw the text. You can even use the exact same font you were using in MFC by creating a CFont instance when you handle WM_CREATE, selecting the font into the DC, and calling wglUseFontBitmaps, which will make a series of rasterized bitmaps that you can use with glCallLists. (While you're at it, call GetCharABCWidths and GetTextMetrics to determine the width and height of each glyph, respectively.)
ABC glyphInfo[256]; // for font widths
TEXTMETRIC tm; // for font heights
// create a bitmap font
CFont myFont;
myFont.CreateFont(
16, // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
_T("Arial") // lpszFacename
);
// change the current font in the DC
CDC* pDC = CDC::FromHandle(hdc);
// make the system font the device context's selected font
CFont *pOldFont = (CFont *)pDC->SelectObject (&myFont);
// the display list numbering starts at 1000, an arbitrary choice
wglUseFontBitmaps (hdc, 0, 255, 1000);
VERIFY( GetCharABCWidths (hdc, 0, 255, &glyphInfo[0]) );
pDC->GetTextMetrics(&tm);
if(pOldFont)
pDC->SelectObject(pOldFont);
myFont.DeleteObject();
Then when you handle WM_PAINT, reset your matrices and use glRasterPos2d to put the text where you need it to go. I suggest calculating the exact width of your string using code similar to the one below if you want it to be horizontally centered.
// indicate start of glyph display lists
glListBase (1000);
CRect r;
GetWindowRect(r);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, r.Width(), 0, r.Height());
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
CString formattedString;
formattedString.Format("Pi is about %1.2f", 3.1415);
int stringWidth=0; // pixels
for(int j=0; j < formattedString.GetLength(); ++j)
stringWidth += glyphInfo[ formattedString.GetAt(j) ].abcA + glyphInfo[ formattedString.GetAt(j) ].abcB + glyphInfo[ formattedString.GetAt(j) ].abcC;
double textXPosition, textYPosition;
textXPosition = r.Width()/2-stringWidth/2; // horizontally centered
textYPosition = r.Height()/2-tm.tmHeight/2; // vertically centered
glRasterPos2d(textXPosition,textYPosition);
// this is what actually draws the text (as a series of rasterized bitmaps)
glCallLists (formattedString.GetLength(), GL_UNSIGNED_BYTE, (LPCSTR)formattedString);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
While the setup is annoying, you only have to do it once, and I think it's less frustrating than dealing with GDI. Mixing GDI and OpenGL is really asking for trouble, and OpenGL does a very good job of displaying text -- you get sub-pixel accuracy for free, among other benefits.
Edit: In response to your request for including GUI elements, I will assume that you meant that you want to have both OpenGL-drawn windows and also standard Windows controls (edit boxes, check boxes, buttons, list controls, etc.) inside the same parent window. I will also assume that you intend OpenGL to draw only part of the window, not the background of the window.
Since you said you're using MFC, I suggest that you create a dialog window, add all of your standard Windows controls to it, and then add in a CWnd-derived class where you handle WM_PAINT. Use the resource editor to move the control to where you want it. Effectively, you're making an owner-draw custom control where OpenGL is doing the drawing. So OpenGL will draw that window, and the standard MFC classes (CEdit, CButton, etc.) will draw themselves. This works well in my experience, and it's really not much different from what GDI does in an owner-draw control.
What if instead you want OpenGL to draw the background of the window, and you want standard Windows controls to appear on top of it? I don't think this is a great idea, but you can handle WM_PAINT and WM_ERASE for your CDialog-derived class. In WM_ERASE, call OpenGL to draw your 3D content, which will be overwritten by the standard Windows controls when WM_PAINT is called. Alternatively in WM_PAINT you could call OpenGL before calling CDialog::OnDraw, which would be similar.
Please clarify your statement "I want to add some 2s graphics overlay (like labels ,gui elements)" if you want me to write more.
Looking at your code I assume the OpenGL rendering is called from a timer or as idel loop action. Naturally OpenGL execution will probably contain some clearing, thus taking anything else drawn with it.
Mixing GDI text drawing with OpenGL is not recommended, but can be done. But of course you then need to include that code into the OpenGL drawing function, too, placing all GDI operations after the buffer swap.

How to draw a part of a window into a memory device context?

I'm using simple statements to keep it, er, simple:
The screen goes from 0, 0 to 1000, 1000 (screen coordinates).
A window goes from 100, 100 to 900, 900 (screen coordinates).
I have a memory device context that goes from 0, 0 to 200, 200 (logical coordinates).
I need to send a WM_PRINT message to the window. I can pass the device context to the window via WM_PRINT, but I cannot pass which part of its window it should draw into the device context.
Is there some way to alter the device context that will result in the window drawing a specific part of itself into the device context (say, its bottom right portion from 700, 700 to 900, 900)?
(This is all under plain old GDI and in C or C++. Any solution must be too.)
Please note:
This problem is part of a larger solution in which the device context size is fixed and speed is crucial, so I cannot draw the window in full into a separate device context and blit the part I want from the resultant full bitmap into my device context.
You can call SetViewportOrgEx() to specify the device context coordinates that will be mapped to the window's origin:
SetViewportOrgEx(yourDC, -600, -600, NULL);
Since your window's size is 800x800, offsetting the DC's coordinate system by -600x-600 will result in the 200x200 bottom right area of the window being drawn, and the rest being clipped.