PDA

View Full Version : Drawing a point at the right 3d position

Bronislaw666
07-09-2017, 03:10 AM
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);

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;

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;

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;
}
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;
}
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);
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;
}

Silence
07-09-2017, 06:11 AM
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 (http://nehe.gamedev.net/article/using_gluunproject/16013/) might help you get all the maths in order to get that vector.

GClements
07-09-2017, 06:40 AM
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);

Bronislaw666
07-09-2017, 08:08 AM
I tried this code and it at least compiles perfectly after some modifications :-)
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.....

GClements
07-09-2017, 10:20 AM
But, when I leftclick, the point is always drawn directly beneath "me"
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.

Bronislaw666
07-09-2017, 12:21 PM
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);
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);
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;
}

GClements
07-09-2017, 02:39 PM
This is likely related to the problem:

sf::Vector2f coord_pos = window.mapPixelToCoords(pixel_pos);

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

Bronislaw666
07-10-2017, 10:57 AM
Ok, solved: Indeed the link: http://nehe.gamedev.net/article/using_gluunproject/16013/ from "Silence" and activating the depth buffer helped.