I am using the following code to implement the "zoom to mouse point" functionality in opengl in c++. Most of the code is from
OpenGL Google maps style 2D camera / zoom to mouse cursor but I had to change some include statements because of changes in the GLM (http://glm.g-truc.net/0.9.5/index.html) codebase.
#include <GL/freeglut.h>
#include <iostream>
using namespace std;
#define GLM_SWIZZLE_XYZW
#define GLM_FORCE_RADIANS
#include "glm/glm.hpp"
#include "glm/detail/setup.hpp"
#include "glm/detail/_swizzle.hpp"
#include "glm/detail/_swizzle_func.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
glm::dvec3 Unproject( const glm::dvec3& win )
{
glm::ivec4 view;
glm::dmat4 proj, model;
glGetDoublev( GL_MODELVIEW_MATRIX, glm::value_ptr( model ) );
glGetDoublev( GL_PROJECTION_MATRIX, glm::value_ptr( proj ) );
glGetIntegerv( GL_VIEWPORT, glm::value_ptr( view ) );
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::swizzle< glm::X, glm::Y >( world1 + u * ( world2 - world1 ) );
}
// pixels per unit
const double ppu = 80.0;
glm::dvec2 center( 0 );
double scale = 1.0;
void ApplyCamera()
{
glMatrixMode( 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 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glScaled( scale, scale, 1.0 );
glTranslated( -center[0], -center[1], 0 );
}
glm::dvec2 centerStart( 0 );
int btn = -1;
void mouse( int button, int state, int x, int y )
{
ApplyCamera();
y = glutGet( GLUT_WINDOW_HEIGHT ) - 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;
if( GLUT_LEFT_BUTTON == btn )
{
ApplyCamera();
glm::dvec2 cur = PlaneUnproject( glm::dvec2( x, y ) );
center += ( centerStart - cur );
}
glutPostRedisplay();
}
void wheel( int wheel, int direction, int x, int y )
{
y = glutGet( GLUT_WINDOW_HEIGHT ) - 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();
glBegin( GL_QUADS );
glVertex2i( -1, -1 );
glVertex2i( 1, -1 );
glVertex2i( 1, 1 );
glVertex2i( -1, 1 );
glEnd();
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 );
glutMainLoop();
return 0;
}
The errors
error: 'swizzle' is not a member of 'glm'
error: 'X' is not a member of 'glm'
error: 'Y' is not a member of 'glm'
are in line
return glm::swizzle< glm::X, glm::Y >( world1 + u * ( world2 - world1 ) );
I know that glm::swizzle is obviously wrong but glm::detail::_swizzle requires 8 parameters compared to 2 earlier by glm::swizzle. What would be the new parameters be? Also, I don't know how to correct glm::X and glm::Y. Can someone please help?
Actually you don't need swizzling at all in this case since there's a dvec2 constructor that can slice off the z coordinate for you.
Change the last line of PlaneUnproject() from this:
return glm::swizzle< glm::X, glm::Y >( world1 + u * ( world2 - world1 ) );
to this:
return glm::dvec2( world1 + u * ( world2 - world1 ) );
I updated the complete code in my original answer.
Though if you really want to swizzle:
Pare down your #includes:
#define GLM_SWIZZLE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
And change the last line of PlaneUnproject() from this:
return glm::swizzle< glm::X, glm::Y >( world1 + u * ( world2 - world1 ) );
to this:
return ( world1 + u * ( world2 - world1 ) ).xy();
In newer version of glm if you want swizzle operator you need to define GLM_FORCE_SWIZZLE before including glm. GLM_SWIZZLE is deprecated.
#define GLM_FORCE_SWIZZLE
#include "glm/glm.hpp"
Then you can use it like this:
return world1.yx();
And on some compiler you can also do it like this:
return world1.yx;
Warning: Just be aware that there is a huge performance impact if you enable it.
On Visual Studio 2013 I measure around x150 speed difference in runtime matrix multiplication.
With swizzle operations: 450 000 multiplications per second.
Without swizzle operations: 65 000 000 multiplications per second.
I believe it's pretty straightforward. Under the glm namespace there's no member known as 'swizzle'. Please check these two points :-
Just check if there is a nested namespace and refer it accordingly like glm::detail::swizzle
Else please check if the member name is swizzle or _swizzle.
Thanks!
Related
I am trying to implement a 2D camera in OpenGL that behaves like the Google maps camera. Specifically the "zoom to mouse point" functionality.
So far I have been able to implement pan and zoom OK - but only if the zoom is locked to the center of the window/widget. If I try to zoom on the mouse location the view seems to "jump" and after the zoom level increases the item I zoomed in on is no longer under the mouse cursor.
My camera class is below - quite a lot of code but I couldn't make it any smaller sorry!
I call Apply() on the start of each frame, and I call SetX/YPos when the scene is panned, finally I call SetScale with the previous scale +/- 0.1f with the mouse position when the mouse wheel is scrolled.
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_matrix();
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_matrix()
{
glViewport(0, 0, m_Width, m_Height);
glMatrixMode(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);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void Camera::Apply()
{
// Zoom
init_matrix();
// 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()
{
}
Update: I seem to have noticed 2 issues:
The mouse position in SetScale is incorrect - I don't know why.
No matter what I try glOrtho causes the centre of the screen to be the zoom point,I confirmed this setting the zoom point manually/hard coding it. In Google maps the screen won't "stick" to the centre like this.
Update again:
I'm also using Qt if this makes any difference, I just have a basic QGLWidget and I am using the mouse wheel event to perform the zoom. I take the delta of the wheel event and then either add or subtract 0.1f to the scale passing in the mouse position from the wheel event.
Get the world-space coordinates of the mouse cursor using the current zoom factor and model/proj/view matrices.
Adjust zoom factor
Get the world-space mouse coordinates again using the new zoom factor
Shift the camera position by the difference in world-space mouse coordinates
Redraw scene using new camera position and zoom factor
Something like this (in the wheel() callback):
#include <GL/freeglut.h>
#include <iostream>
using namespace std;
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
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()
{
glMatrixMode( 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 );
glMatrixMode( 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;
}
I am trying to draw the shape shown below (in black) in OpenGL (for desktops, not mobile). I'd prefer it in 3D, but 2D would suffice as well. I will be mapping a texture to it as well. I've used triangle fans/strips for other things in the past and I imagine that's what I'd need to use here but I'm just not sure where to start on it. I've drawn the WHITE part before, but never the inverse (the black part). Any thoughts or guidance on what to use (triangle fan, triangle strip, some other odd OpenGL shape I probably didn't know existed, etc...)
Final Solution:
void draw_arch(GLfloat width, GLfloat height, GLint slices)
{
glPushMatrix();
GLfloat offset = 0.5f;
glScalef(width/2,height/(1+offset),1.0f);
glBegin(GL_QUADS);
for( unsigned int i = 0; i < slices; ++i ) {
float curAngle = ( ( i + 0 ) / (float)slices ) * 3.14159;
float nxtAngle = ( ( i + 1 ) / (float)slices ) * 3.14159;
glVertex2f( cos( curAngle ), sin( curAngle ) );
glVertex2f( cos( curAngle ), 1.0f + offset );
glVertex2f( cos( nxtAngle ), 1.0f + offset );
glVertex2f( cos( nxtAngle ), sin( nxtAngle ) );
}
glEnd();
glPopMatrix();
}
I can adjust the "offset" variable to make different looking arches, however in this application, I choose 0.5 to make it look the way I wanted it to!
Generate the top half of a circle and an offset above it and link the two with quads/triangles:
#include <GL/glut.h>
#include <cmath>
void glShape( const float height, unsigned int segs )
{
glBegin( GL_QUADS );
for( unsigned int i = 0; i < segs; ++i )
{
float curAngle = ( ( i + 0 ) / (float)segs ) * 3.14159;
float nxtAngle = ( ( i + 1 ) / (float)segs ) * 3.14159;
glVertex2f( cos( curAngle ), sin( curAngle ) );
glVertex2f( cos( curAngle ), 1 + height );
glVertex2f( cos( nxtAngle ), 1 + height );
glVertex2f( cos( nxtAngle ), sin( nxtAngle ) );
}
glEnd();
}
void display()
{
glClearColor( 0, 0, 0, 1 );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
glOrtho( -2 * ar, 2 * ar, -2, 2, -1, 1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
glShape( 0.1f, 20 );
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
Probably not the minimal number of quads/triangles but it's quick and easy :)
how can drawing small circle in side big circle using algorthim of circle
each circle consist of 9 or 8 point and link each 4point to gather and link this with 4 under and so on
.................................................
........................................................
You need to generate the inner and outer points in one loop.
Give this a shot:
#include <GL/glut.h>
#include <cmath>
void Torus2d( float inner, float outer, unsigned int pts )
{
glBegin( GL_QUAD_STRIP );
for( unsigned int i = 0; i <= pts; ++i )
{
float angle = ( i / (float)pts ) * 3.14159f * 2.0f;
glVertex2f( inner * cos( angle ), inner * sin( angle ) );
glVertex2f( outer * cos( angle ), outer * sin( angle ) );
}
glEnd();
}
void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
glOrtho( -4 * ar, 4 * ar, -4, 4, -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
Torus2d( 2, 3, 20 );
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
Find below a faster (less trigono) solution in Go (can be easily translated to c++) which was inspired by Sieglord's Abode article
func Torus(cx, cy, inner, outer float64, num_segments int) {
theta := 2 * math.Pi / float64(num_segments)
c := math.Cos(theta) //precalculate the sine and cosine
s := math.Sin(theta)
t_in := 0.0
t_out := 0.0
x_in := inner //we start at angle = 0
x_out := outer //we start at angle = 0
y_in := 0.0
y_out := 0.0
gl.Begin(gl.QUAD_STRIP)
for ii := 0; ii <= num_segments; ii++ {
gl.Vertex2f(float32(x_in+cx), float32(y_in+cy)) //output vertex inner
gl.Vertex2f(float32(x_out+cx), float32(y_out+cy)) //output vertex outer
//apply the rotation matrix
t_in = x_in
t_out = x_out
x_in = c*x_in - s*y_in
x_out = c*x_out - s*y_out
y_in = s*t_in + c*y_in
y_out = s*t_out + c*y_out
}
gl.End()
}
Currently I am using glOrtho to zoom and pan around a 2D graph I am rendering.
I have setup up the viewport to the standard width and height. Then I set the glOrtho so that my frustrum makes screen coordinates match world coordinates.
glViewport(0, 0, window_width,window_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, window_width,window_height,0 , 100, -100);
When I do my zoom function in my mouse callback, I multiply the frustrum edges by a zoom factor....
glOrtho( 0 * zoomOut,
window_width * zoomOut,
window_height * zoomOut,
0 * zoomOut,
100, -100);
My question is.... how do I zoom using mouse position as a centre?
I have tried this... (where mouseStoreX and mouseStoreY are the position stored at the first click)
glOrtho( (0 -mouseStoreX )* zoomOut + mouseStoreX,
(window_width - mouseStoreX) * zoomOut + mouseStoreX,
(window_height - mouseStoreY) * zoomOut + mouseStoreY,
(0 - mouseStoreY) * zoomOut + mouseStoreY,
100, -100);
It seems to work but the frustrum jumps around when I do a new click. I think somewhere I'm not taking into account the zoomOut factor when doing the mouse position storing.
EDIT: here is my latest code which I am still struggling with...
void ZoomOrtho(){ //ON MOUSE CLICK.....
if (zooming == false){
keyStore.LMx = keyStore.Mx; //store mouse pos for next comparison
keyStore.LMy = keyStore.My;
//get mouse pos
mouseStoreX = keyStore.Mx;//mouse pos at this moment
mouseStoreY = keyStore.My;
//get current projection matrices
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
//flip Y for opengl reasons
winY = (float)viewport[3] - winY;
//get world mouse coordinate
gluUnProject( mouseStoreX, mouseStoreY , 0.0, modelview, projection, viewport, &posX_,&posY_, &posZ_);
// calc difference between mouse world pos and centre of 'camera'
dx = posX_ - FS.centerX;
dy = posY_ - FS.centerY;
}
//ON DRAG......
zooming = true;
//do mouse movement detection and increment zoomOut
//#################################################
int xDiff = keyStore.Mx - keyStore.LMx; //mouse drag difference in screen space just for incrementing zoom
int yDiff = keyStore.My - keyStore.LMy; //
if (xDiff > 0 && (zoomFactor >= 0.5 ) ) {
zoomFactor -= zoomInc;
if (zoomFactor < 0.5 ) {zoomFactor = 0.5;}
}
else if (xDiff < 0 && (zoomFactor <= 2.0 )) {
zoomFactor += zoomInc;
if (zoomFactor > 2.0){zoomFactor = 2.0;}
}
//#################################################
//fill structure with clipping plane values. zooms ortho projection and keeps mouse pos anchored.
FS.left = ((FS.centerX - dx - (window_width/2.0))*zoomFactor) +dx;
FS.right = ((FS.centerX -dx + (window_width/2.0))*zoomFactor)+dx ;
FS.bottom = ((FS.centerY -dy + (window_width/2.0))*zoomFactor)+dy;
FS.top = ((FS.centerY -dy - (window_width/2.0))*zoomFactor) +dy;
// store last mouse pos for next comparison.
keyStore.LMx = keyStore.Mx;
keyStore.LMy = keyStore.My;
}
void zoomRelease(){
cout << " releasing" << std::endl;
//set zoom to false so we know we are not draggin mouse anymore.
zooming = false;
keyStore.LMx = 0;
keyStore.LMy = 0;
// recenter by taking midpoint between new left and right clipping planes so dx has a reference point
FS.centreX = (FS.right-FS.left)/2.0;
}
void DrawGui(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(FS.left, FS.right,FS.bottom, FS.top, 1, -1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
//do drawing
}
Give this a shot:
// g++ main.cpp -o main -lglut -lGL && ./main
#include <GL/glut.h>
double centerX = 0, centerY = 0;
double width = 0, height = 0;
void mouse( int button, int state, int mx, int my )
{
// flip mouse y axis so up is +y
my = glutGet( GLUT_WINDOW_HEIGHT ) - my;
// convert mouse coords to (-1/2,-1/2)-(1/2, 1/2) box
double x = ( mx / (double)glutGet( GLUT_WINDOW_WIDTH ) ) - 0.5;
double y = ( my / (double)glutGet( GLUT_WINDOW_HEIGHT ) ) - 0.5;
if( GLUT_UP == state )
{
double preX = ( x * width );
double preY = ( y * height );
double zoomFactor = 1.5;
if( button == GLUT_LEFT_BUTTON )
{
// zoom in
width /= zoomFactor;
height /= zoomFactor;
}
if( button == GLUT_RIGHT_BUTTON )
{
// zoom out
width *= zoomFactor;
height *= zoomFactor;
}
double postX = ( x * width );
double postY = ( y * height );
// recenter
centerX += ( preX - postX );
centerY += ( preY - postY );
}
glutPostRedisplay();
}
void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho
(
centerX - ( width / 2.0 ),
centerX + ( width / 2.0 ),
centerY - ( height / 2.0 ),
centerY + ( height / 2.0 ),
-1,
1
);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
glBegin( GL_TRIANGLES );
glVertex2i( 0, 0 );
glVertex2i( 150, 0 );
glVertex2i( 0, 150 );
glVertex2i( 0, 0 );
glVertex2i( -150, 0 );
glVertex2i( 0, -150 );
glEnd();
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 600, 600 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMouseFunc( mouse );
width = glutGet( GLUT_WINDOW_WIDTH );
height = glutGet( GLUT_WINDOW_HEIGHT );
glutMainLoop();
return 0;
}
I'm assuming that when you do the second click you're storing its value into mouseStoreXY. If so, this is causing the jumping. You were drawing with an offset of the old mouseStoreXY and you suddenly offset to the new one.
The solution will be to store the projection matrix inputs persistently and then modify them incrementally on each frame.
Give this a shot:
// g++ main.cpp -o main -lglut -lGL && ./main
#include <GL/glut.h>
#include <cmath>
void getMouseCoords( int mx, int my, double& x, double& y )
{
// flip mouse y axis so up is +y
my = glutGet( GLUT_WINDOW_HEIGHT ) - my;
// convert mouse coords to (-1/2,-1/2)-(1/2, 1/2) box
x = ( mx / (double)glutGet( GLUT_WINDOW_WIDTH ) ) - 0.5;
y = ( my / (double)glutGet( GLUT_WINDOW_HEIGHT ) ) - 0.5;
}
int btn;
double baseX, baseY;
double baseWidth, baseHeight;
double centerX = 0, centerY = 0;
double width = 0, height = 0;
void mouse( int button, int state, int mx, int my )
{
baseWidth = width;
baseHeight = height;
btn = button;
getMouseCoords( mx, my, baseX, baseY );
}
void motion( int mx, int my )
{
if( btn != GLUT_LEFT_BUTTON )
{
return;
}
double x, y;
getMouseCoords( mx, my, x, y );
double preX = ( baseX * width );
double preY = ( baseY * height );
double zoomFactor = exp( baseY - y );
width = baseWidth * zoomFactor;
height = baseHeight * zoomFactor;
double postX = ( baseX * width );
double postY = ( baseY * height );
// recenter
centerX += ( preX - postX );
centerY += ( preY - postY );
glutPostRedisplay();
}
void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho
(
centerX - ( width / 2.0 ),
centerX + ( width / 2.0 ),
centerY - ( height / 2.0 ),
centerY + ( height / 2.0 ),
-1,
1
);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
glBegin( GL_TRIANGLES );
glVertex2i( 0, 0 );
glVertex2i( 150, 0 );
glVertex2i( 0, 150 );
glVertex2i( 0, 0 );
glVertex2i( -150, 0 );
glVertex2i( 0, -150 );
glEnd();
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 600, 600 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMouseFunc( mouse );
glutMotionFunc( motion );
width = glutGet( GLUT_WINDOW_WIDTH );
height = glutGet( GLUT_WINDOW_HEIGHT );
glutMainLoop();
return 0;
}
If you wanted to go a different route and simply use glTranslate and gluPerspective, you can acheive the same effect. The mouse event for the scroll wheel (using PyOpenGL) might look something like:
def MouseWheelScroll(self, event):
"""Called when the mouse's scroll wheel is scrolled up or down. This modifies the zoomFactor
which renders the graphics closer or further away on the screen. It also translates the graphics
slightly based on the position of the mouse. This creates an effect of zooming to the location
of the mouse on the screen.
"""
scrolledUp = event.GetWheelRotation() # Returns positive for up, negative for down
self.x, self.y = event.GetPosition()
viewport = glGetIntegerv(GL_VIEWPORT)
width = viewport[2]
height = viewport[3]
centerX = width / 2.0
centerY = height / 2.0
# Make sure cursor is on the screen
if ((self.x > 0 and self.x < width) and (self.y > 0 and self.y < height)):
if (scrolledUp > 0):
self.zoomFactor -= 2.0
self.translation[0] -= (self.x - centerX)
self.translation[1] += (self.y - centerY)
else:
self.zoomFactor += 2.0
self.translation[0] += (self.x - centerX)
self.translation[1] += (self.y - centerY)
if (self.zoomFactor > 150.0):
self.zoomFactor = 150.0
elif (self.zoomFactor < 0.1):
self.zoomFactor = 0.1
self.Refresh(False)
You would then just need to translate the graphics, set up your perspective, and then render the scene.
# Translate graphics
glTranslatef(0.0, 0.0, (self.translation[1]/100.0) * (math.tan(self.cameraPosition[0]/self.cameraPosition[1])))
glTranslatef(0.0, (self.translation[0]/100.0) * (math.tan(self.cameraPosition[0]/self.cameraPosition[1])), 0.0)
# Set Perspective
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(self.zoomFactor, float(width)/float(height), self.nearPlane, self.farPlane)
# Render Scene
glMatrixMode(GL_MODELVIEW)
...Draw stuff here...
I need to draw a Polygon iteratively. for example, I want to draw a Polygon with 8 corners. I need to draw the first line with GL_LINES and then draw the second line with the same length and an angle of 135° between them, the third line has also an angle of 135° to the second line, etc.
I want to make a loop to render it but I don't know how. I have an approach, but it doesn't work properly.
the second point of line n-1 should be the first point of n and so on...
At the end, I need to get a closed Polygon. the last point of the last line should be the first point of the first line.
Use GL_LINE_LOOP, that will connect your last vertex to your first automatically:
#include <GL/glut.h>
#include <cmath>
void glPolygon( unsigned int sides )
{
if( sides < 3 ) return;
const float PI = 3.14159;
const float step = ( 2 * PI ) / static_cast< float >( sides );
glBegin( GL_LINE_LOOP );
for( unsigned int i = 0; i < sides; ++i )
{
glVertex2f( cos( i * step ), sin( i * step ) );
}
glEnd();
}
void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double ar = glutGet( GLUT_WINDOW_WIDTH ) / (double)glutGet( GLUT_WINDOW_HEIGHT );
glOrtho( -2 * ar, 2 * ar, -2, 2, -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
glPolygon( 8 );
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Polygons" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
If you're dead-set on GL_LINES this works:
void glPolygonLines( unsigned int sides )
{
if( sides < 3 ) return;
const float PI = 3.14159f;
const float step = ( 2 * PI ) / static_cast< float >( sides );
glBegin( GL_LINES );
for( unsigned int i = 0; i < sides; ++i )
{
unsigned int cur = ( i + 0 ) % sides;
unsigned int nxt = ( i + 1 ) % sides;
glVertex2f( cos( cur * step ), sin( cur * step ) );
glVertex2f( cos( nxt * step ), sin( nxt * step ) );
}
glEnd();
}