glReadPixels doesnt work for the first left click - c++

I am working on a MFC app which is a MDI. One of the child frame uses OpenGL(mixed with fixed function and modern version) called 3d view and another child frame uses GDI called plan view. Both of the views use the same doc.
The 3d view has a function to detect if the mouse cursor is over rendered 3d model by reading pixels and check its depth value.
The function is used for WM_MOUSEMOVE and WM_LBUTTONDOWN events. Most time it works pretty well. But it failed when I move my cursor from the plan view(currently active) to the 3d view and left mouse click. The depth values read from the pixels(called from onLButtonDown) are always all zeros though it is over a model. There is no OpenGL error reported. It only fails on the first mouse click when the 3d view is not activated. Afterwards, everything works well again.
The issue doesn't happen on all machines. And it happens to me but not to another guy with the same hardware machine with me. Is that possible hardware related or a code bug?
Things tried:
I tried to increase the pixel block size much bigger but depths are still all zero.
If I click on the title bar of the 3d view to activate it first, then works.
I tried to set the 3d view active and foreground in the onLButtonDown method before reading pixels. But still failed.(btw, the 3d view should be active already before the OnLButtonDown handler via other message handler fired by the left button down).
I tried to invalidate rect before reading pixels, failed too.
The code is as below:
BOOL CMy3DView::IsOverModel(int x0, int y0, int &xM, int &yM, GLfloat &zWin, int width0 , int height0 )
{
int width = max(1,width0);
int height= max(1,height0);
CRect RectView;
GetClientRect(&RectView);
GLint realy = RectView.Height() - 1 - (GLint)y0 ; /* OpenGL y coordinate position */
std::vector<GLfloat> z(width*height);
//Read the window z co-ordinates the z value of the points in a rectangle of width*height )
xM = max(0, x0-(width-1)/2);
yM = max(0, realy-(height-1)/2);
glReadPixels(xM, yM, (GLsizei)width, (GLsizei)height, GL_DEPTH_COMPONENT, GL_FLOAT, &z[0]); OutputGlError(_T("glReadPixels")) ;
/* check pixels along concentric, nested boxes around the central point */
for (int k=0; k<=(min(height,width)-1)/2; ++k){
for (int i=-k;i<=k;++i){
xM = x0+i;
for (int j=-k;j<=k;++j){
if (abs(i)==k || abs(j)==k) {
yM = realy+j;
zWin=z[(i+(width-1)/2)+width*(j+(height-1)/2)];
if (zWin<1.0-FLT_EPSILON) break;
}
}
if (zWin<1.0-FLT_EPSILON) break;
}
if (zWin<1.0-FLT_EPSILON) break;
}
yM = RectView.Height() - 1 - yM;
if (zWin>1.0-FLT_EPSILON || zWin<FLT_EPSILON) {// z is the depth, between 0 and 1, i.e. between Near and Far plans.
xM=x0; yM=y0;
return FALSE;
}
return TRUE;
}

Just found a solution for that: I called render(GetDC) before any processing in OnLButtonDown. somehow it fixed the issue though I don't think it's necessary.
InvalideRect wont fix the issue since it will update the view for the next WM_PAINT.
Weird, since it works for some machines without the fix. Still curious about the reason.

Related

SFML sf::View::move inconstancy

UPDATE:
I couldn't figure out the exact problem, however I made a fix that's good enough for me: Whenever the player's X value is less then half the screen's width, I just snap the view back to the center (up left corner) using sf::View::setCenter().
So I'm working on a recreating of Zelda II to help learn SFML good enough so I can make my own game based off of Zelda II. The issue is the screen scrolling, for some reason, if link walks away from the wall and initiated the camera to follow him, and then move back toward the wall, the camera won't go all the way back to the end of the wall, which occurs on the other wall at the end of the scene/room. This can be done multiple times to keep making the said camera block get further away from the wall. This happens on both sides of the scene, and I have reason to believe it has something to do with me trying to make the game frame independent, here's an included GIF of my issue to help understand:
My camera function:
void Game::camera() {
if (this->player.getVar('x') >= this->WIDTH / 2 and this->player.getVar('x') < this->sceneWidth - this->WIDTH / 2) {
this->view.move(int(this->player.getVar('v') * this->player.dt * this->player.dtM), 0);
}
}
player.getVar() is a temporary function I'm using to get the players x position and x velocity, using the argument 'x' returns the players x position, and 'v' returns the x velocity. WIDTH is equal to 256, and sceneWidth equals 767, which is the image I'm using for the background's width. dt and dtM are variables for the frame independence I mentioned earlier, this is the deceleration:
sf::Clock sclock;
float dt = 0;
float dtM = 60;
int frame = 0;
void updateTime() {
dt = sclock.restart().asSeconds();
frame += 1 * dt * dtM;
}
updateTime() is called every frame, so dt is updated every frame as well. frame is just a frame counter for Link's animations, and isn't relevant to the question. Everything that moves and is rendered on the screen is multiplied by dt and dtM respectively.
There's a clear mismatch between the movement of the player and the one of the camera... You don't show the code to move the player, but if I guess you don't cast to int the movement there, as you are doing on the view.move call. That wouldn't be a problem if you were setting the absolute position of the camera, but as you are constantly moving it, the little offset accumulates each frame, causing your problem.
One possible solution on is to skip the cast, which is unnecessary because sf::View::move accepts float as arguments.
void Game::camera() {
if (this->player.getVar('x') >= this->WIDTH / 2 and this->player.getVar('x') < this->sceneWidth - this->WIDTH / 2) {
this->view.move(this->player.getVar('v') * this->player.dt * this->player.dtM, 0);
}
}
Or even better, not to use view.move but to directly set the position of the camera each frame. Something like:
void Game::camera() {
if (this->player.getVar('x') >= this->WIDTH / 2 and this->player.getVar('x') < this->sceneWidth - this->WIDTH / 2) {
this->view.setCenter(this->player.getVar('x'), this->view.getCenter().y);
}
}

Change Cursor Image While Hovering Over An Object Drawn At OpenGL And Displaying Object's Variables

I am currently working on a project that I use C++, OpenGL, Qt 5.9.2 and Microsoft Visual Studio Professional 2015 on a 64 bit Operating System, Windows 10 Pro.
I have a user interface that I have created and in that user interface, there is a QGLWidget, that I am using for draw processes, with other widgets like push buttons and a dockWidget. I have a class Ball and it has variables(distance(double) and angle(int)) that determines where an instance of that Ball is going to be drawn inside the QGLWidget. Ball class has got 2 more variables that is, id(int), model(String) and year(int) Every other draw process draws lines except Ball drawings.
Drawings are 2 dimensional.
Every Ball has the same color(rgb)!
First problem: I want to left click to one of the Ball instances and I want to display it's id, model and year at The dockWidget.
Second Problem: While doing the stuff that I have mentioned at the First Problem section. I want the cursor image to change while hovering above any of the Ball instances, and change back to default Windows mouse cursor while not.
I have created a function that checks if the MouseEvent is LeftClick:
void DisplayManager::mousePressEvent(QMouseEvent* ev) {
if (ev->buttons() & Qt::LeftButton) { // Balls Are Green
if(// CHECK IF THERE IS A BALL AT THE CLICKED COORDINATES) {
// DISPLAY THE X and Y OF THE BALL AT THE DOCK WIDGET
}
}
}
This is my initializeGL function: (DisplayManager is the name of my QGLWidget)
void DisplayManager::initializeGL() {
glEnable(GL_COLOR_MATERIAL); // Enables the changing of the draw color with glColor() functions
glColor3f(0.0, 1.0, 0.0);
glEnable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 1); //sets a black background 1 0 0 1
}
On the basis this is a Picking problem and there are several information about it at the internet but I am not using GLUT and I am not using any shader. So in the light of all these I was unable to find any effective solution or clue about how can I accomplish all that I want.
I would be really glad if someone could help me with at least one of these problems.
I have currently finished working with the project. I thought that I should provide an answer to my question in case someone with a similar problem comes across with my question in the future.
void DisplayManager::mousePressEvent(QMouseEvent* ev) {
// The processes that are being executed after a left mouse button click on the DisplayManager.
if (ev->buttons() & Qt::LeftButton) {
double aspectRatio = openGLWidgetWidth / openGLWidgetHeight;
int xOfClicked = ev->x() - (openGLWidgetWidth / 2);
int yOfClicked = - (ev->y() - (openGLWidgetHeight / 2));
// The variable to be used for calculating fault tolerance of the click event.
int distanceBetweenPressAndDraw = 0;
// Executes the processes inside for every ball in vector.
for (int i = 0; i < ballVector.length(); i++) {
// Calculates the screen coordinates of the i'th ball.
int rangeOfBallInScreenDistance = rangeToScreenDistance(ballVector.at(i).range);
double screenXOfBall = rangeOfBallInScreenDistance * sin(ballVector.at(i).degree * DEGTORAD);
double screenYOfBall = rangeOfBallInScreenDistance * cos(ballVector.at(i).degree * DEGTORAD);
// Calculates the distance between pressed position and the i'th ball according to the screen coordinates.
distanceBetweenPressAndDraw = sqrt(pow((screenXOfBall - xOfClicked), 2) + pow((screenYOfBall - yOfClicked), 2));
// Decides if the clicked position is a ball (considering the fault tolerance).
if (distanceBetweenPressAndDraw < 10) {
emit printXY(QPointF(xOfClicked, yOfClicked)); // Prints the screen coordinates of the clicked positions (At the Dock Widget inside Main Window).
}
}
}
}
This was the solution for my First Problem. I would be glad though if someone could answer my Second problem in a comment or answer somehow.

Drawing keeps getting stuck to the screen in SDL

In my program what I am drawing gets stuck on to the screen I am drawing on, by this I mean that what I previously drawed onto the screen stays after I call SDL_UpdateWindowSurface(). Here is my code.
void tower_manager::render()
{
m_tower.draw(camx, camy,m_screen);
//SDL_BlitSurface(test, NULL, m_screen, NULL);
SDL_Rect rect = { 32, 32, 32, 32 };
//draw the tower walls;
for (int x = 0; x < towerWidth; x++)
{
for (int y = 0; y < towerHeight * 2; y += 2)
{
rect.x = x*blockSize - camx;
rect.y = y*blockSize - camy;
SDL_BlitSurface(test, NULL, m_screen, &rect);
}
}
SDL_UpdateWindowSurface(m_window);
}
Apparently I need at least 10 reputation to post images so I cant post a screen shot but here is an example, you know what happens to the desktop when a windows application freezes and it keeps drawing the same window over and over again and you can draw it around to make art and stuff? That's exactly what it looks like is happening here. Also I have another issue when I call the tower objects method that is originally going to draw the tower using the same code it does not draw or do anything at all(i am passing in a pointer to the screen I am drawing to in its parameter).
You would want to clear the surface regions that you are drawing to. If you don't, then the screen surface retains the old renderings from previous frames and you are drawing on top of them. This causes a smearing artifact.
An old optimization (no longer so useful with SDL2 or OpenGL) here is to keep track of dirty rectangles and clear each of them, but the simplest way is to just clear the entire surface each frame before rendering.
So, once per frame do something like this:
SDL_FillRect(m_screen, NULL, 0x000000);

SDL Screen Render Position Error Along Top of Window

I am making a top down isometric game using SDL 2.0 and C++ and have come across a glitch.
When a texture is rendered to the screen using the SDL_RenderCopyfunction, the moment the top of the texture hits the top of the screen it gets pushed down by one pixel, thus causing the missing borders seen in the following picture:
Pre-edit with no annotations
Post-edit with with annotations
The following is my render function specific to the world itself, as the world renders differently from everything else in the game, because I am simply copying a "source" texture instead of loading a texture for every single tile in the game, which would be absurdly inefficient.
//-----------------------------------------------------------------------------
// Rendering
DSDataTypes::Sint32 World::Render()
{
//TODO: Change from indexing to using an interator (pointer) for efficiency
for(int index = 0; index < static_cast<int>(mWorldSize.mX * mWorldSize.mY); ++index)
{
const int kTileType = static_cast<int>(mpTilesList[index].GetType());
//Translate the world so that when camera panning occurs the objects in the world will all be in the accurate position
I am also incorporating camera panning as follows (paraphrased with some snippets of code included as my camera panning logic spans multiple files due to the object orientated design of my game):
(code from above immediately continued below)
mpTilesList[index].SetRenderOffset(Window::GetPanOffset());
//position (dstRect)
SDL_Rect position;
position.x = static_cast<int>(mpTilesList[index].GetPositionCurrent().mX + Window::GetPanOffset().mX);
position.y = static_cast<int>(mpTilesList[index].GetPositionCurrent().mY + Window::GetPanOffset().mY);
position.w = static_cast<int>(mpTilesList[index].GetSize().mX);
position.h = static_cast<int>(mpTilesList[index].GetSize().mY);
//clip (frame)
SDL_Rect clip;
clip.x = static_cast<int>(mpSourceList[kTileType].GetFramePos().mX);
clip.y = static_cast<int>(mpSourceList[kTileType].GetFramePos().mY);
clip.w = static_cast<int>(mpSourceList[kTileType].GetFrameSize().mX);
clip.h = static_cast<int>(mpSourceList[kTileType].GetFrameSize().mY);
I am confused as to why this is happening, as regardless of whether I include my simple culling algorithm or not (as shown below), the same result occurs.
(code from above immediately continued below)
//Check to ensure tile is being drawn within the screen size. If so, rendercopy it, else simply skip over and do not render it.
//If the tile's position.x is greather than the left border of the screen
if(position.x > (-mpSourceList[kTileType].GetRenderSize().mX))
{
//If the tile's position.y is greather than the top border of the screen
if(position.y > (-mpSourceList[kTileType].GetRenderSize().mY))
{
//If the tile's position.x is less than the right border of the screen
if(position.x < Window::msWindowSize.w)
{
//If the tile's position.y is less than the bottom border of the screen
if(position.y < Window::msWindowSize.h)
{
SDL_RenderCopy(Window::mspRenderer.get(), mpSourceList[kTileType].GetTexture(), &clip, &position);
}
}
}
}
}
return 0;//TODO
}
You may have a rounding error when you are casting the positions to ints. Perhaps you should round to the nearest integer instead of just taking the floor (which is what you're cast is doing). A tile at position (0.8, 0.8) will be rendered at pixel (0, 0) when it should probably be rendered at position (1, 1).
Or you could ensure that the size of your tiles is always an integer, then errors shouldn't accumulate.
Short version of answer, restated at bottom:
By fixing my data types issue, that allowed me to fix my math library, which removed the issue of rounding parts of pixels, since there is no such thing as less than 1 but greater than 0 pixels on the screen when rendering.
Long Answer:
I believe the issue to have been caused by a rounding error with the offset logic used when rotating a 2D grid to a diagonal isometric perspective, with the rounding error only occurring when dealing with screen coordinates between -1 and +1.
Since I based the conversion from changing an orthogonal grid to a diagonal grid on the y axis (rows), this would explain why the single pixel offset was occurring only at the top border of the screen and not bottom border.
Even though every single row had implicit rounding occurring without any safety checks, only the conversion from world coordinates to screen coordinates dealt with rounding between a positive and negative number.
The reason behind all of this is because my math library which was templatized had an issue a lot of my code being based on type defined user types such as:
typedef unsigned int Uint32;
typedef signed int Sint32;
So I simply used DSMathematics::Vector2<float> instead of the proper implementation of DSMathematics::Vector2<int>.
The reason this is an issue is because there cannot be "half a pixel" on the screen, and thus integers must be used instead of floating point values.
By fixing my data types issue, that allowed me to fix my math library, which removed the issue of rounding parts of pixels, since there is no such thing as less than 1 but greater than 0 pixels on the screen when rendering.

Window resizing and scaling images / Redeclaring back buffer size / C++ / DIRECTX 9.0

C++ / Windows 8 / Win api / DirectX 9.0
I am having real big issues with this:
https://github.com/jimmyt1988/TheGame/tree/master/TheGame
Problem is that I have defined some adjust coordinate functions. They are for when a window is resized and I need to offset all of my coordinates so that my mouse cooridnates are working out the correct collisions and also to scale and yet keep ratio locked for the images I am drawing to the screen.
For example, If I had a screen at 1920 x 1080 and then resized to 1376 x 768, I need to make sure that the bounding boxes for my objects (for when my mouse hovers over them) is adjusted on the mouse coordinates I use to use to check if the mouse was in the bounding box.
I found out that I originally had problems because when I resized my window, directX was automatically scaling everything.. and on top of that, I too was rescaling things, so they would get utterly screwed... I was told by someone that I need to re-declare my screen buffer width and height, which I have done keeping in mind there is a border to my window and also a menu at the top.
Can anyone see why... regardless of doing all this stuff, I am still getting the incorrect results.
If you manage to run my application: Pressing the 1 key will make the resolution 1920 x 1080, pressing the 2 key will make it 1376 x 768. The resize is entirely wrong: https://github.com/jimmyt1988/TheGame/blob/master/TheGame/D3DGraphics.cpp
float D3DGraphics::ResizeByPercentageChangeX( float point )
{
float lastScreenWidth = screen.GetOldWindowWidth();
float currentScreenWidth = screen.GetWindowWidth();
if( lastScreenWidth > currentScreenWidth + screen.GetWidthOffsetOfBorder() )
{
float percentageMoved = currentScreenWidth / lastScreenWidth;
point = point * percentageMoved;
}
return point;
}
float D3DGraphics::ResizeByPercentageChangeY( float point )
{
float lastScreenHeight = screen.GetOldWindowHeight();
float currentScreenHeight = screen.GetWindowHeight();
if( lastScreenHeight > currentScreenHeight + screen.GetHeightOffsetOfBorderAndMenu() )
{
float percentageMoved = currentScreenHeight / lastScreenHeight;
point = point * percentageMoved;
}
return point;
}
and yet if you put the return point above this block of code and just do nothing to it, it scales perfectly because of blooming directX regardless of this which is being called correctly (presparams are previously declared in the D3DGraphics construct and a reference held in the class its self:
void D3DGraphics::ResizeSequence()
{
presParams.BackBufferWidth = screen.GetWindowWidth() - screen.GetWidthOffsetOfBorder();
presParams.BackBufferHeight = screen.GetWindowHeight() - screen.GetHeightOffsetOfBorderAndMenu();
d3dDevice->Reset( &presParams );
}
This is the problem at hand:
Here is the code that makes this abomination of a rectangle:
void Game::ComposeFrame()
{
gfx.DrawRectangle( 50, 50, screen.GetWindowWidth() - screen.GetWidthOffsetOfBorder() - 100, screen.GetWindowHeight() - screen.GetHeightOffsetOfBorderAndMenu() - 100, 255, 0, 0 );
}
EDIT::::::::::::::::
I noticed that On MSDN it says:
Before calling the IDirect3DDevice9::Reset method for a device, an
application should release any explicit render targets, depth stencil
surfaces, additional swap chains, state blocks, and D3DPOOL_DEFAULT
resources associated with the device.
I have now released the vbuffer and reinstantiated it after the presparams and device are reset.
EDIT::::::::::::
I placed an HRESULT on my reset in which I now manage to trigger an error... But, well.. it doesn't really help me! : http://i.stack.imgur.com/lqQ5K.jpg
Basically, the issue was I was being a complete derp. I was putting into my rectangle the window width and then readjusting that size based on the oldwidth / newwidth.. well the new width was already the screen size... GRRRRRRR.