OpenGL Google maps style caméra 2D / zoom sur le curseur de la souris

J’essaie d’implémenter une caméra 2D dans OpenGL qui se comporte comme la caméra Google Maps. Spécifiquement la fonctionnalité “zoom au sharepoint la souris”.

Jusqu’à présent, j’ai pu mettre en œuvre le panoramique et le zoom, mais seulement si le zoom est verrouillé au centre de la fenêtre / du widget. Si j’essaie de zoomer sur l’emplacement de la souris, la vue semble “sauter” et, une fois le niveau de zoom augmenté, l’élément sur lequel j’ai zoomé n’est plus sous le curseur de la souris.

Ma classe d’appareils photo est ci-dessous – pas mal de code mais je ne pouvais pas le réduire, désolé!

J’appelle Apply() au début de chaque image et j’appelle SetX/YPos lorsque la scène est balayée, enfin j’appelle SetScale avec l’échelle précédente +/- 0.1f avec la position de la souris lorsque la molette de la souris est défilée.


camera.h

 class Camera { public: Camera(); void Apply(); void SetXPos(float xpos); void SetYPos(float ypos); void SetScale(float scaleFactor, float mx, float my); float XPos() const { return m_XPos; } float YPos() const { return m_YPos; } float Scale() const { return m_ScaleFactor; } void SetWindowSize(int w, int h); void DrawTestItems(); private: void init_masortingx(); float m_XPos; float m_YPos; float m_ScaleFactor; float m_Width; float m_Height; float m_ZoomX; float m_ZoomY; }; 

camera.cpp

 Camera::Camera() : m_XPos(0.0f), m_YPos(0.0f), m_ScaleFactor(1.0f), m_ZoomX(0.0f), m_ZoomY(0.0f), m_Width(0.0f), m_Height(0.0f) { } // Called when window is created and when window is resized void Camera::SetWindowSize(int w, int h) { m_Width = (float)w; m_Height = (float)h; } void Camera::init_masortingx() { glViewport(0, 0, m_Width, m_Height); glMasortingxMode(GL_PROJECTION); glLoadIdentity(); float new_W = m_Width * m_ScaleFactor; float new_H = m_Height * m_ScaleFactor; // Point to zoom on float new_x = m_ZoomX; float new_y = m_ZoomY; glOrtho( -new_W/2+new_x, new_W/2+new_x, new_H/2+new_y, -new_H/2+new_y, -1,1); glMasortingxMode(GL_MODELVIEW); glLoadIdentity(); } void Camera::Apply() { // Zoom init_masortingx(); // Pan glTranslatef( m_XPos, m_YPos, 1.0f ); DrawTestItems(); } void Camera::SetXPos(float xpos) { m_XPos = xpos; } void Camera::SetYPos(float ypos) { m_YPos = ypos; } // mx,my = window coords of mouse pos when wheel was scrolled // scale factor goes up or down by 0.1f void Camera::SetScale(float scaleFactor, float mx, float my) { m_ZoomX = (float)mx; m_ZoomY = (float)my; m_ScaleFactor = scaleFactor; } void Camera::DrawTestItems() { } 

Mise à jour: il semble que j’ai remarqué 2 problèmes:

  1. La position de la souris dans SetScale est incorrecte – je ne sais pas pourquoi.
  2. Peu importe ce que j’essaie de faire avec glOrtho, le centre de l’écran est le sharepoint zoom, j’ai confirmé ce réglage manuellement ou avec un codage dur. Dans Google Maps, l’écran ne “collera” pas au centre de cette manière.

Mettre à jour à nouveau:

J’utilise aussi Qt si cela fait une différence, j’ai juste un QGLWidget de base et j’utilise l’événement molette de la souris pour effectuer le zoom. Je prends le delta de l’événement wheel et ensuite j’ajoute ou soustrais 0.1f à l’échelle passant dans la position de la souris à partir de l’événement wheel.

  1. Obtenez les coordonnées spatiales du curseur de la souris en utilisant le facteur de zoom actuel et les masortingces modèle / projet / vue
  2. Ajuster le facteur de zoom
  3. Obtenez à nouveau les coordonnées de la souris dans l’espace mondial en utilisant le nouveau facteur de zoom
  4. Décalage de la position de la caméra de la différence des coordonnées de la souris dans l’espace mondial
  5. Redessiner la scène en utilisant la nouvelle position de la caméra et le facteur de zoom

Quelque chose comme ceci (dans la wheel() rappel):

 #include  #include  using namespace std; #include  #include  glm::dvec3 Unproject( const glm::dvec3& win ) { glm::ivec4 view; glm::dmat4 proj, model; glGetDoublev( GL_MODELVIEW_MATRIX, &model[0][0] ); glGetDoublev( GL_PROJECTION_MATRIX, &proj[0][0] ); glGetIntegerv( GL_VIEWPORT, &view[0] ); glm::dvec3 world = glm::unProject( win, model, proj, view ); return world; } // unprojects the given window point // and finds the ray intersection with the Z=0 plane glm::dvec2 PlaneUnproject( const glm::dvec2& win ) { glm::dvec3 world1 = Unproject( glm::dvec3( win, 0.01 ) ); glm::dvec3 world2 = Unproject( glm::dvec3( win, 0.99 ) ); // u is a value such that: // 0 = world1.z + u * ( world2.z - world1.z ) double u = -world1.z / ( world2.z - world1.z ); // clamp u to reasonable values if( u < 0 ) u = 0; if( u > 1 ) u = 1; return glm::dvec2( world1 + u * ( world2 - world1 ) ); } // pixels per unit const double ppu = 1.0; glm::dvec2 center( 0 ); double scale = 1.0; void ApplyCamera() { glMasortingxMode( GL_PROJECTION ); glLoadIdentity(); const double w = glutGet( GLUT_WINDOW_WIDTH ) / ppu; const double h = glutGet( GLUT_WINDOW_HEIGHT ) / ppu; glOrtho( -w/2, w/2, -h/2, h/2, -1, 1 ); glMasortingxMode( GL_MODELVIEW ); glLoadIdentity(); glScaled( scale, scale, 1.0 ); glTranslated( -center[0], -center[1], 0 ); } glm::dvec2 mPos; glm::dvec2 centerStart( 0 ); int btn = -1; void mouse( int button, int state, int x, int y ) { ApplyCamera(); y = glutGet( GLUT_WINDOW_HEIGHT ) - y; mPos = glm::ivec2( x, y ); btn = button; if( GLUT_LEFT_BUTTON == btn && GLUT_DOWN == state ) { centerStart = PlaneUnproject( glm::dvec2( x, y ) ); } if( GLUT_LEFT_BUTTON == btn && GLUT_UP == state ) { btn = -1; } glutPostRedisplay(); } void motion( int x, int y ) { y = glutGet( GLUT_WINDOW_HEIGHT ) - y; mPos = glm::ivec2( x, y ); if( GLUT_LEFT_BUTTON == btn ) { ApplyCamera(); glm::dvec2 cur = PlaneUnproject( glm::dvec2( x, y ) ); center += ( centerStart - cur ); } glutPostRedisplay(); } void passiveMotion( int x, int y ) { y = glutGet( GLUT_WINDOW_HEIGHT ) - y; mPos = glm::ivec2( x, y ); glutPostRedisplay(); } void wheel( int wheel, int direction, int x, int y ) { y = glutGet( GLUT_WINDOW_HEIGHT ) - y; mPos = glm::ivec2( x, y ); ApplyCamera(); glm::dvec2 beforeZoom = PlaneUnproject( glm::dvec2( x, y ) ); const double scaleFactor = 0.90; if( direction == -1 ) scale *= scaleFactor; if( direction == 1 ) scale /= scaleFactor; ApplyCamera(); glm::dvec2 afterZoom = PlaneUnproject( glm::dvec2( x, y ) ); center += ( beforeZoom - afterZoom ); glutPostRedisplay(); } void display() { glClearColor( 0, 0, 0, 1 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); ApplyCamera(); glm::dvec2 cur = PlaneUnproject( mPos ); cout << cur.x << " " << cur.y << " " << scale << endl; glPushMatrix(); glScalef( 50, 50, 1 ); glBegin( GL_QUADS ); glColor3ub( 255, 255, 255 ); glVertex2i( -1, -1 ); glVertex2i( 1, -1 ); glVertex2i( 1, 1 ); glVertex2i( -1, 1 ); glEnd(); glPopMatrix(); glutSwapBuffers(); } int main( int argc, char **argv ) { glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE ); glutInitWindowSize( 600, 600 ); glutCreateWindow( "GLUT" ); glutMouseFunc( mouse ); glutMotionFunc( motion ); glutMouseWheelFunc( wheel ); glutDisplayFunc( display ); glutPassiveMotionFunc( passiveMotion ); glutMainLoop(); return 0; }