Drawing a point at the right 3d position

Hi people,

I’ve got a problem, for which I couldn’t find any solution so far, despite searching the whole internet for several days and trying different formulas.
It’s about clicking inside the world and drawing a “point” at the right 3d x/z-position (y-position in 3d coordinates should always be at 0).

What I’ve got so far, are the sfml mouse-coordinates, but I don’t know the formula for getting the right x,z position, when I click somewhere in the 3d world.
The question is: How to draw the red point at the right position in the 3d world without using any external libraries? (that don’t work for me anyway :-)).
Many thx in advance for any help.

This is the full code example, where you can rotate and move around freely (the problem is in the line with the formula “pointx = …, pointz =…”:
I’m sure, there is a simple solution for it without thousands of lines of code…

#include <SFML/Graphics.hpp>
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>
#include <SFML/OpenGL.hpp>

const float height = 1000;
const float width = height*0.75f;

void myGluPerspective(double fovy, double aspect, double zNear, double zFar)
{
    double f = 1.0 / tan(fovy * M_PI / 360);

	double xform[16] =
    {
        f / aspect, 0, 0, 0,
        0,          f, 0, 0,
		0,          0, (zFar + zNear)/(zNear - zFar), -1,
	    0,          0, (2*zFar*zNear)/(zNear - zFar), 0
    };
    glMultMatrixd(xform);

}

float camposx, camposy, camposz, cammovex, cammovey, cammovez = 0;
short rotationsvariable = 0;
float pointx = 0;
float pointz = 1;

int main()
{
	sf::RenderWindow window(sf::VideoMode(height, width, 32), "SFML OpenGL", sf::Style::Close);
	
	window.setFramerateLimit(40);
    glClearColor(0.8f, 0.8f, 0.8f, 0);
	glClearDepth(1);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	myGluPerspective(60,height/width,0.1f,180);

	glTranslatef(0,0,-3);
	camposz = -3;
	
    while (window.isOpen())
    {
        sf::Event event;

		while (window.pollEvent(event))
		{
			switch (event.type)
			{
			case sf::Event::Closed:
				window.close();
				break;
			case sf::Event::KeyPressed:
				if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
				{
					GLfloat matrixf[16];
					glGetFloatv(GL_MODELVIEW_MATRIX, matrixf);

					cammovex = (float)sin(rotationsvariable*M_PI/180)*-0.2f;			
					cammovez = (float)cos(rotationsvariable*M_PI/180)*0.2f;
					camposx = camposx + cammovex;
					camposz = camposz + cammovez;

					glLoadIdentity();  
					myGluPerspective(60,height/width,0.1f,180);
					glRotatef(rotationsvariable, 0, 1.0f, 0);
					glTranslatef(camposx,0,camposz);
					break;
				}
				if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
				{
					GLfloat matrixf[16];
					glGetFloatv(GL_MODELVIEW_MATRIX, matrixf);

					cammovex = (float)sin(rotationsvariable*M_PI/180)*-0.2f;			
					cammovez = (float)cos(rotationsvariable*M_PI/180)*0.2f;
					camposx = camposx - cammovex;
					camposz = camposz - cammovez;

					glLoadIdentity();  
					myGluPerspective(60,height/width,0.1f,180);
					glRotatef(rotationsvariable, 0, 1.0f, 0);
					glTranslatef(camposx,0,camposz);
					break;
				}
				if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
				{
					GLfloat matrixf[16];
					glGetFloatv(GL_MODELVIEW_MATRIX, matrixf);

					rotationsvariable = rotationsvariable - 10;

					if(rotationsvariable==-360)
					{
						rotationsvariable = 0;
					}
					glLoadIdentity();  
					myGluPerspective(60,height/width,0.1f,180);
					glRotatef(rotationsvariable, 0, 1.0f, 0);
					glTranslatef(camposx,0,camposz);
					break;
				}
				if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
				{
					rotationsvariable = rotationsvariable + 10;

					if(rotationsvariable==360)
					{
						rotationsvariable = 0;
					}
					glLoadIdentity();  
					myGluPerspective(60,height/width,0.1f,180);
					glRotatef(rotationsvariable, 0, 1.0f, 0);
					glTranslatef(camposx,0,camposz);
					break;
				}
			case sf::Event::MouseButtonPressed:
				if (event.mouseButton.button == sf::Mouse::Left) 
				{
					sf::Vector2i pixel_pos = sf::Mouse::getPosition(window);
					sf::Vector2f coord_pos = window.mapPixelToCoords(pixel_pos);

					pointx = -camposx+(coord_pos.x/650)*2.0f-1.5f;			// test
					//pointz = ......?
				}
			}
		}
		
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glColor3f(1,1,1); 
		glBegin(GL_QUADS);
		glVertex3f(-1,-1,-1);
		glVertex3f(-1,-1,1);
		glVertex3f(1,-1,1);
		glVertex3f(1,-1,-1);
		glEnd();

 		glColor3f(1,0,0); 
		glPointSize(10);
		glBegin(GL_POINTS);
        glVertex3f(pointx, -1.0f, pointz);
        glEnd( );

        window.display();
    }

    return 0;
}

From where you click, you have an infinite number of points (all along the line from the view center, directed by the vector where you clicked).
This might help you get all the maths in order to get that vector.


void screen_to_obj(
    double screen_x, double screen_y,
    double plane[4],
    double *obj_x, double *obj_y, double *obj_z)
{
    GLdouble modelview[16], projection[16];
    GLint viewport[4];
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetIntegerv(GL_VIEWPORT, viewport);

    GLdouble x1, y1, z1;
    gluUnProject(screen_x, screen_y,  0.0,  modelview,  projection,  viewport,  &x1, &y1, &z1);
    GLdouble x2, y2, z2;
    gluUnProject(screen_x, screen_y,  1.0,  modelview,  projection,  viewport,  &x2, &y2, &z2);

    double k1 = plane[0]*x1 + plane[1]*y1 + plane[2]*z1 + plane[3];
    double k2 = plane[0]*x2 + plane[1]*y2 + plane[2]*z2 + plane[3];
    double t = -k1/(k2-k1);
    *obx_x = x1 + t*(x2-x1);
    *obx_y = y1 + t*(y2-y1);
    *obx_z = z1 + t*(z2-z1);
}
...
double plane[4] = {0,1,0,0}; // for Y=0
screen_to_obj(sx, sy, plane, &ox, &oy, &oz);

I tried this code and it at least compiles perfectly after some modifications :slight_smile:
But, when I leftclick, the point is always drawn directly beneath “me”.


double plane[4] = {0,1,0,0};
screen_to_obj(coord_pos.x, coord_pos.y, plane, &pointx, &pointy, &pointz);

Did even try to set screen_x to width and so on…

Check that you’re using the coordinates correctly. Also, check that the model-view and projection matrices are set correctly at the point you call obj_to_screen() (here, “correctly” means having the same values as when you use the coordinates with glVertex() or whatever).

Note that the result is undefined if the viewpoint is on the Y=0 plane. Also note that if the screen coordinates are “above the horizon” (for the Y=0 plane), the calculated point will be behind the viewpoint.

I experimented with the code, but I got stuck not knowing what to do now. For example: &obj_x,&obj_y,&obj_z don’t work. What is it supposed to be? Is it possible to click anywhere in the world with this code? (not only on the small quad)
Plz help.


#include <SFML/Graphics.hpp>
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>
#include <SFML/OpenGL.hpp>
#include <gl\glu.h>
#pragma comment(lib, "glu32")

const float height = 1000;
const float width = 1000;

void myGluPerspective(double fovy, double aspect, double zNear, double zFar)
{
	double f = 1.0 / tan(fovy * M_PI / 360);
	
	double xform[16] =
	{
		f / aspect, 0, 0, 0,
		0,          f, 0, 0,
		0,          0, (zFar + zNear)/(zNear - zFar), -1,
		0,          0, (2*zFar*zNear)/(zNear - zFar), 0
	};
	glMultMatrixd(xform);
	
}

double pointx, pointy, pointz = 0;

void screen_to_obj(
double screen_x, double screen_y,
double plane[4],
double *obj_x, double *obj_y, double *obj_z)
{
	GLdouble modelview[16], projection[16];
	GLint viewport[4];
	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	glGetIntegerv(GL_VIEWPORT, viewport);
	
	GLdouble x1, y1, z1;
	gluUnProject(screen_x, screen_y,  0.0,  modelview,  projection,  viewport,  &x1, &y1, &z1);
	GLdouble x2, y2, z2;
	gluUnProject(screen_x, screen_y,  1.0,  modelview,  projection,  viewport,  &x2, &y2, &z2);
	
	double k1 = plane[0]*x1 + plane[1]*y1 + plane[2]*z1 + plane[3];
	double k2 = plane[0]*x2 + plane[1]*y2 + plane[2]*z2 + plane[3];
	double t = -k1/(k2-k1);
	*obj_x = x1 + t*(x2-x1);
	*obj_y = y1 + t*(y2-y1);
	*obj_z = z1 + t*(z2-z1);
}

int main()
{
	sf::RenderWindow window(sf::VideoMode(height, width, 32), "SFML OpenGL", sf::Style::Close);
	glClearDepth(1);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	myGluPerspective(60,height/width,0.1f,180);
	glTranslatef(0,0,-3);
	
    while (window.isOpen())
    {
        sf::Event event;
		while (window.pollEvent(event))
		{
			switch (event.type)
			{
			case sf::Event::Closed:
				window.close();
				break;
			case sf::Event::MouseButtonPressed:
				if (event.mouseButton.button == sf::Mouse::Left) 
				{
					sf::Vector2i pixel_pos = sf::Mouse::getPosition(window);
					sf::Vector2f coord_pos = window.mapPixelToCoords(pixel_pos);

					GLdouble modelview[16];
					glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
					GLdouble projection[16];
					glGetDoublev (GL_PROJECTION_MATRIX, projection);

					GLint viewport[4];
					glViewport(0,0,1000,1000);
					glGetIntegerv(GL_VIEWPORT, viewport);

					double plane[4] = {0,1,0,0}; // for Y=0
					//screen_to_obj(sx, sy, plane, &ox, &oy, &oz);
					screen_to_obj(coord_pos.x,coord_pos.y, plane,&obj_x,&obj_y,&obj_z);
				}
			}
		}
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glColor3f(1,1,1); 
		glBegin(GL_QUADS);
		glVertex3f(-1,-1,-1);
		glVertex3f(-1,-1,1);
		glVertex3f(1,-1,1);
		glVertex3f(1,-1,-1);
		glEnd();
		glColor3f(1,0,0); 
		glPointSize(10);
		glBegin(GL_POINTS);
		glVertex3f(pointx, -1.0f, pointz);
		glEnd( );
		window.display();
    }
    return 0;
}

This is likely related to the problem:

If you’re going to mix SFML and OpenGL, I think you’ll need to ask on the SFML forums.

Ok, solved: Indeed the link: http://nehe.gamedev.net/article/using_gluunproject/16013/ from “Silence” and activating the depth buffer helped.