opengl - Rotating around a sphere using vectors and NOT glulookat - opengl

I'm having an issue with drawing a model and rotating it using the mouse,
I'm pretty sure there's a problem with the mathematics but not sure .
The object just rotates in a weird way.
I want the object to start rotating each click from its current spot and not reset because of the
vectors are now changed and the calculation starts all over again.
void DrawHandler::drawModel(Model * model){
unsigned int l_index;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW); // Modeling transformation
glLoadIdentity();
Point tempCross;
crossProduct(tempCross,model->getBeginRotate(),model->getCurrRotate());
float tempInner= innerProduct(model->getBeginRotate(),model->getCurrRotate());
float tempNormA =normProduct(model->getBeginRotate());
float tempNormB=normProduct(model->getCurrRotate());
glTranslatef(0.0,0.0,-250.0);
glRotatef(acos (tempInner/(tempNormA*tempNormB)) * 180.0 / M_PI,tempCross.getX(),tempCross.getY(),tempCross.getZ());
glColor3d(1,1,1);
glBegin(GL_TRIANGLES);
for (l_index=0;l_index < model->getTrianglesDequeSize() ;l_index++)
{
Triangle t = model->getTriangleByPosition(l_index);
Vertex a1 = model->getVertexByPosition(t.getA());
Vertex a2 = model->getVertexByPosition(t.getB());
Vertex a3 = model->getVertexByPosition(t.getC());
glVertex3f( a1.getX(),a1.getY(),a1.getZ());
glVertex3f( a2.getX(),a2.getY(),a2.getZ());
glVertex3f( a3.getX(),a3.getY(),a3.getZ());
}
glEnd();
}
This is the mouse function which saves the beginning vector of the rotating formula
void Controller::mouse(int btn, int state, int x, int y)
{
x=x-WINSIZEX/2;
y=y-WINSIZEY/2;
if (btn==GLUT_LEFT_BUTTON){
switch(state){
case(GLUT_DOWN):
if(!_rotating){
_model->setBeginRotate(Point(float(x),float(y),
(-float(x)*x - y*y + SPHERERADIUS*SPHERERADIUS < 0)? 0:float(sqrt(-float(x)*x - y*y + SPHERERADIUS*SPHERERADIUS))));
_rotating=true;
}
break;
case(GLUT_UP):
_rotating=false;
break;
}
}
}
and finally the following function which holds the current vector.
(the beginning vector is where the mouse was clicked at
and the curr vector is where the mouse position at the moment )
void Controller::getMousePosition(int x,int y){
x=x-WINSIZEX/2;
y=y-WINSIZEY/2;
if(_rotating){
_model->setCurrRotate(Point(float(x),float(y),
(-float(x)*x - y*y + SPHERERADIUS*SPHERERADIUS < 0)? 0:float(sqrt(-float(x)*x - y*y + SPHERERADIUS*SPHERERADIUS))));
}
}
where sphereradius is the sphere radius O_O of 70 degress
is any calculation wrong ? cant seem to find the problem...
thanks

Why so complicated? Either you change the view matrix or you change the model matrix of your focused object. If you choose to change the model matrix and your object is centered in (0,0,0) of the world coordinate system, computing the rotation around a sphere illusion is trivial - you just rotate into the opposite direction. If you want to change the view matrix (which is actually done when you change the position of the camera) you have to approximate the surface points on the chosen sphere. Therefore, you could introduce two parameters specifying two angles. Everytime you click move your mouse, you update the params and compute the new locations on the sphere. There are some useful equations in [http://en.wikipedia.org/wiki/Sphere].

Without knowing what library (or libraries) you're using your code is rather difficult to read. It seems you're setting up your camera at (0, 0, -250), looking towards the origin, then rotating around the origin by the angle between two vectors, model->getCurrRotate() and model->getBeginRotate().
The problem seems to be that in "mouse down" events you explicitly set BeginRotate to the point on the sphere under the mouse, then in "mouse move" events you set CurrRotate to the point under the mouse, so every time you click somewhere else, you lose the previous state of rotation because BeginRotate and CurrRotate are simply overwritten.
Combining multiple rotations around arbitrary different axes is not a trivially simple task. The proper way to do it is to use quaternions. You may find this primer on quaternions and other 3D math concepts useful.
You might also want a more robust algorithm for converting screen coordinates to model coordinates on the sphere. The one you are using is assuming the sphere appears 70 pixels in radius on the screen and that the projection matrix is orthographic.

Related

How to rotate a cube by its center

I am trying to rotate a "cube" full of little cubes using keyboard which works but not so great.
I am struggling with setting the pivot point of rotation to the very center of the big "cube" / world. As you can see on this video, center of front (initial) face of the big cube is the pivot point for my rotation right now, which is a bit confusing when I rotate the world a little bit.
To explain it better, it looks like I am moving initial face of the cube when using keys to rotate the cube. So the pivot point might be okay from this point of view, but what is wrong in my code? I don't understand why it is moving by front face, not the entire cube by its very center?
In case of generating all little cubes, I call a function in 3 for loops (x, y, z) and the function returns cubeMat so I have all cubes generated as you can see on the video.
cubeMat = scale(cubeMat, {0.1f, 0.1f, 0.1f});
cubeMat = translate(cubeMat, {positioning...);
For rotation itself, a short example of rotation to left looks like this:
mat4 total_rotation; //global variable - never resets
mat4 rotation; //local variable
if(keysPressed[GLFW_KEY_LEFT]){
timer -= delta;
rotation = rotate(mat4{}, -delta, {0, 1, 0});
}
... //rest of key controls
total_rotation *= rotation;
And inside of those 3 for cycles is also this:
program.setUniform("ModelMatrix", total_rotation * cubeMat);
cube.render();
I have read that I should use transformation to set the pivot point to the middle but in this case, how can I set the pivot point inside of little cube which is in center of world? That cube is obviously x=2, y=2, z=2 since in for cycles, I generate cubes starting at x=0.
You are accumulating the rotation matrices by right-multiplication. This way, all rotations are performed in the local coordinate systems that result from all previous transformations. And this is why your right-rotation results in a turn after an up-rotation (because it is a right-rotation in the local coordinate system).
But you want your rotations to be in the global coordinate system. Thus, simply revert the multiplication order:
total_rotation = rotation * total_rotation;

Rotating object around itself

I have an object that I want to to move around using the following mechanic: the left and right arrows change its rotation and the up arrow increments its position.
My problem is that I either can't rotate the object around itself, or I can't move it in the direction being looked at.
The draw function is as follows:
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glScalef(SCALE, SCALE, SCALE);
glTranslatef(x, 0, 0);
glRotatef(rotationZ, 0, 0, 1);
glTranslatef(-x, 0, 0);
// Draw the object...
glPopMatrix();
Key press detection code:
case GLUT_KEY_UP:
teclas.up = GL_TRUE;
glutPostRedisplay();
break;
case GLUT_KEY_LEFT:
teclas.left = GL_TRUE;
glutPostRedisplay();
break;
case GLUT_KEY_RIGHT:
teclas.right = GL_TRUE;
glutPostRedisplay();
break;
Timer function:
if (teclas.up) {
x++;
}
if (teclas.left) {
rotationZ++;
}
if (teclas.right) {
rotationZ--;
}
glutPostRedisplay();
I've seen multiple threads about this, and I've tried changing the signal of the x variable but nothing seems to work.
Edit(solved):
I just changed the part of the Timer function that is responsible for the forward movement to this:
if (estado.teclas.up) {
homer.x+= (float)cos(homer.rotationZ * M_PI / 180);
homer.y+= (float)sin(homer.rotationZ * M_PI / 180);
}
And also, my Draw function:
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glScalef(SCALE, SCALE, SCALE);
glTranslatef(x, 0, 0);
glRotatef(rotationZ, 0, 0, 1);
// Draw the object...
glPopMatrix();
This way, the object always moves towards what it's facing
This is a case of problem with Moving Reference Frame, those are the keywords. Unless you simulate physics of process as well, for OpenGL rendering all we have to worry about are the coordinates. Here we have the stationary reference frame, sometimes called a world frame (especially if observer is moving relative to it as well), and a moving reference frame (MRF )connected to object. MRF can have arbitrary rotation and translation relative to world frame, there are traditional ways how it is defined.
For example for Earth globe MRF defined as origin in center of Earth, positive X axis intersecting equator and 0 meridian, positive Z - north pole and Y is complementary to them. For static point on surface of earth (local geographic coordinates) it usually Y directed to zenith and positive Z - toward North in plane of horizon and positive X - toward east. In case of moving vehicle's the positive Y- or pitch axis always points to its left, and the positive Z- or yaw axis always points up, X - the roll axis is pointed straight forward. This one seem to match your case.
Regardless of axis specification, the rotation of vehicle is equivalent of changing matrix corresponding to it. Lets call it transformation matrix. In local coordinates vehicle speed v = {vx,0,0} is a vector collinear to positive X axis. But in world coordinates it is equal to
v' = M*v
where M is a transformation matrix of MRF. As v is change of coordinates per unit of time, then any translations should follow this formula too. There are two ways to solve this , if you're using legacy OpenGL, you have two options:
First: you would start with identity matrix and recreate all transforms in proper order.
Set identity matrix.
Translate by value required (in local cords)
Apply rotations of vehicle
Translate by values of last known position of vehicle.
Either calculate new position of vehicle, knowing transforms, or read that value , by getting matrix from OpenGL (by glGetFloatv(GL_MODELVIEW_MATRIX, ptr)) and extracting offset from it.
Downside of this method is that you have to use functions of OpenGL,where each call of glTranslate or glRotate is creating another matrix that is getting multiplied with other (in opposite order). That's excess math operations and precision of them isn't brilliant either. It can get quite interesting in Chinese manner if you have several frames of reference, especially nested.
Second method is to do all matrix math yourself, for example using some math library like GLM (glm.h) and store matrix for each frame of reference, modifying or regenerating them when needed. You can supply matrix directly to OpenGL even in legacy mode by glLoadMatrix. If you worry about performance, you should know that all modern implementations are done that math on CPU anyway, GPUs do not work with matrix stack anymore, for long time. It can be found quickly by inspecting open-source implementations.
In case of modern, flexible pipeline you don't have glScale, glTranslate, glRotate available at all. Entire matrix stack is deprecated in OpenGL 3. You can do it only in second way, but in this case you would supply matrices to shader program through uniforms.

OpenGL Raycasting with any object

I'm just wondering if there was any way which one can perform mouse picking detection onto any object. Whether it would be generated object or imported object.
[Idea] -
The idea I have in mind is that, there would be iterations with every object in the scene. Checking if the mouse ray has intersected with an object. For checking the intersection, it would check the mouse picking ray with the triangles that make up the object.
[Pros] -
I believe the benefit of this approach is that, every object can be detected with mouse picking since they all inherit from the detection method.
[Cons] -
I believe this drawbacks are mainly the speed and the method being very expensive. So would need fine tuning of optimization.
[Situation] -
In the past I have read about mouse picking and I too have implemented some basic form of mouse picking. But all those were crappy work which I am not proud of. So again today, I have re-read some of the stuff from online. Nowadays I see alot of mouse picking using color ids and shaders. I'm not too keen for this method. I'm more into a mathematical side.
So here is my mouse picking ray thingamajig.
maths::Vector3 Camera::Raycast(s32 mouse_x, s32 mouse_y)
{
// Normalized Device Coordinates
maths::Vector2 window_size = Application::GetApplication().GetWindowSize();
float x = (2.0f * mouse_x) / window_size.x - 1.0f;
float y = 1.0f;
float z = 1.0f;
maths::Vector3 normalized_device_coordinates_ray = maths::Vector3(x, y, z);
// Homogeneous Clip Coordinates
maths::Vector4 homogeneous_clip_coordinates_ray = maths::Vector4(normalized_device_coordinates_ray.x, normalized_device_coordinates_ray.y, -1.0f, 1.0f);
// 4D Eye (Camera) Coordinates
maths::Vector4 camera_ray = maths::Matrix4x4::Invert(projection_matrix_) * homogeneous_clip_coordinates_ray;
camera_ray = maths::Vector4(camera_ray.x, camera_ray.y, -1.0f, 0.0f);
// 4D World Coordinates
maths::Vector3 world_coordinates_ray = maths::Matrix4x4::Invert(view_matrix_) * camera_ray;
world_coordinates_ray = world_coordinates_ray.Normalize();
return world_coordinates_ray;
}
I have this ray plane intersection function which calculates if a certain ray as intersected with a certain plane. DUH!
Here is the code for that.
bool Camera::RayPlaneIntersection(const maths::Vector3& ray_origin, const maths::Vector3& ray_direction, const maths::Vector3& plane_origin, const maths::Vector3& plane_normal, float& distance)
{
float denominator = plane_normal.Dot(ray_direction);
if (denominator >= 1e-6) // 1e-6 = 0.000001
{
maths::Vector3 vector_subtraction = plane_origin - ray_origin;
distance = vector_subtraction.Dot(plane_normal);
return (distance >= 0);
}
return false;
}
There are many more out there. E.g. Plane Sphere Intersection, Plane Disk Intersection. These things are like very specific. So it feel that is very hard to do mouse picking intersections on a global scale. I feel this way because, for this very RayPlaneIntersection function. What I expect to do with it is, retrieve the objects in the scene and retrieve all the normals for that object (which is a pain in the ass). So now to re-emphasize my question.
Is there already a method out there which I don't know, that does mouse picking in one way for all objects? Or am I just being stupid and not knowing what to do when I have everything?
Thank you. Thank you.
Yes, it is possible to do mouse-picking with OpenGL: you render all the geometry into a special buffer that stores a unique id of the object instead of its shaded color, then you just look at what value you got at the pixel below the mouse and know the object by its id that is written there. However, although it might be simpler, it is not a particularly efficient solution if your camera or geometry constantly moves.
Instead, doing an analytical ray-object intersection is the way to go. However, you don't need to check the intersection of every triangle of every object against the ray. That would be inefficient indeed. You should cull entire objects by their bounding boxes, or even portions of the whole scene. Game engines have their own spacial index data structure to speed-up ray-object intersections. They need it not only for mouse picking, but also for collision-detection, physics simulations, AI, and what-not.
Also note that the geometry used for the picking might be different from the one used for rendering. One example that comes to mind is that of semi-transparent objects.

How to rotate an object according to it's orientation

Similar question for WebGL: Rotate object around world axis .
I need to rotate an object in a way that the user should be able to move it with the mouse, like if he's dragging it. The problem is that glRotatef just rotates the object without taking account of it's orientation. For WebGL the solution was to use quaternions, but I guess that there aren't quaternions in OpenGL.
This is how I achieve a rotation for now:
// rotation 2D GLfloat C-vector, posX, posY GLfloat's
void mouse(int button, int state, int x, int y) {
if(button== GLUT_LEFT_BUTTON) {
if(state== GLUT_DOWN)
{
posX=x;
posY= y;
onEvent= true;
}
else if(state== GLUT_UP)
{
GLfloat dx= x-posX;
GLfloat dy= y-posY;
rotation[0]= -dy/5;
rotation[1]= dx/5;
onEvent= false;
glutPostRedisplay();
}
}
}
Then I handle the rotation in the display function:
glPushMatrix();
glRotatef(rotation[0],1,0,0);
glRotatef(rotation[1],0,1,0);
// Draw the object
glPopMatrix();
It kinda works, but like I said it should like if the user is able to drag the object to rotate it. Instead if for example the object is rotated of 90 degrees around the X axis, when the user drags the mouse horizontally to make it rotate around the Y axis, it rotates in the inverse direction. I need an idea here, how could I do that?
Edit
I tried to use glMultMatrixf, but the object doesn't rotate correctly: it gets scaled instead of rotating, this is the code I've edited in the mouse function:
// Global variables:
// GLfloat xRotationMatrix[4][4];
// GLfloat yRotationMatrix[4][4];
else if(state== GLUT_UP && onEvent)
{
GLfloat dx= (x-posX)/(180.0*5)*2.0*M_PI;
GLfloat dy= (y-posY)/(180.0*5)*2.0*M_PI;
// Computing rotations
double cosX= cos(dx);
double sinX= sin(dy);
double cosY= cos(dy);
double sinY= sin(dy);
// x axis rotation
xRotationMatrix[1][1]+= cosY;
xRotationMatrix[1][2]+=-sinY;
xRotationMatrix[2][2]+= sinY;
xRotationMatrix[2][2]+= cosY;
// y axis rotation
yRotationMatrix[0][0]+= cosX;
yRotationMatrix[0][2]+= sinX;
yRotationMatrix[2][0]+= -sinX;
yRotationMatrix[2][2]+= cosX;
onEvent= false;
glutPostRedisplay();
}
Then in the display function:
glPushMatrix();
glMultMatrixf((const GLfloat*)xRotationMatrix);
glMultMatrixf((const GLfloat*)yRotationMatrix);
glutSolidTeapot(10);
glPopMatrix();
This is the non rotated teapot:
If I drag the mouse horizontally to rotate the teapot around the y axis, instead of the rotation this is what I get:
First of all a bit of algebra.
Let v be a vector, M your current modelview matrix, and R the matrix associated with a glRotate command. Then, if you use glRotate, what you get is:
M * R * v
That means you are rotating around object axes. You want to rotate around the world axes, that is:
R * M * v
See the difference? Unfortunately GL doesn't have a MatrixPreMult function.
In modern OpenGL we don't use the matrix stack anymore, in fact while working with shaders we manually pass the transformation matrices to the GL program. What (most) people do is write/use an external vector algebra library (like Eigen).
One possible (untested) workaround which uses only the old deprecated GL stuffs may be something like this:
void rotate(float dx, float dy)
{
//assuming that GL_MATRIX_MODE is GL_MODELVIEW
float oldMatrix[4][4];
glGetFloatv(GL_MODELVIEW_MATRIX,oldMatrix);
glLoadIdentity();
glRotatef(-dy,1,0,0);
glRotatef(dx,0,1,0);
glMultMatrixf(oldMatrix);
}
And you put this code in your mouse function, not in the draw routine.
You can use this trick by keeping the view matrix in the GL matrix stack, then pushing/popping everytime you have to draw an object. I wouldn't recommend something like that in a large project.
Notice also that if you invert the order of the two glRotate calls in the code above you can get slightly different results, expecially if dx and dy are not small.
This code might be slightly better:
float angle = sqrt(dx*dx+dy*dy)*scalingFactor;
glRotate(angle,-dy,dx,0);

Preserving rotations in OpenGL

I'm drawing an object (say, a cube) in OpenGL that a user can rotate by clicking / dragging the mouse across the window. The cube is drawn like so:
void CubeDrawingArea::redraw()
{
Glib::RefPtr gl_drawable = get_gl_drawable();
gl_drawable->gl_begin(get_gl_context());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
{
glRotated(m_angle, m_rotAxis.x, m_rotAxis.y, m_rotAxis.z);
glCallList(m_cubeID);
}
glPopMatrix();
gl_drawable->swap_buffers();
gl_drawable->gl_end();
}
and rotated with this function:
bool CubeDrawingArea::on_motion_notify_event(GdkEventMotion* motion)
{
if (!m_leftButtonDown)
return true;
_3V cur_pos;
get_trackball_point((int) motion->x, (int) motion->y, cur_pos);
const double dx = cur_pos.x - m_lastTrackPoint.x;
const double dy = cur_pos.y - m_lastTrackPoint.y;
const double dz = cur_pos.z - m_lastTrackPoint.z;
if (dx || dy || dz)
{
// Update angle, axis of rotation, and redraw
m_angle = 90.0 * sqrt((dx * dx) + (dy * dy) + (dz * dz));
// Axis of rotation comes from cross product of last / cur vectors
m_rotAxis.x = (m_lastTrackPoint.y * cur_pos.z) - (m_lastTrackPoint.z * cur_pos.y);
m_rotAxis.y = (m_lastTrackPoint.z * cur_pos.x) - (m_lastTrackPoint.x * cur_pos.z);
m_rotAxis.z = (m_lastTrackPoint.x * cur_pos.y) - (m_lastTrackPoint.y * cur_pos.x);
redraw();
}
return true;
}
There is some GTK+ stuff in there, but it should be pretty obvious what it's for. The get_trackball_point() function projects the window coordinates X Y onto a hemisphere (the virtual "trackball") that is used as a reference point for rotating the object. Anyway, this more or less works, but after I'm done rotating, and I go to rotate again, the cube snaps back to the original position, obviously, since m_angle will be reset back to near 0 the next time I rotate. Is there anyway to avoid this and preserve the rotation?
Yeah, I ran into this problem too.
What you need to do is keep a rotation matrix around that "accumulates" the current state of rotation, and use it in addition to the rotation matrix that comes from the current dragging operation.
Say you have two matrices, lastRotMx and currRotMx. Make them members of CubeDrawingArea if you like.
You haven't shown us this, but I assume that m_lastTrackPoint is initialized whenever the mouse button goes down for dragging. When that happens, copy currRotMx into lastRotMx.
Then in on_motion_notify_event(), after you calculate m_rotAxis and m_angle, create a new rotation matrix draggingRotMx based on m_rotAxis and m_angle; then multiply lastRotMx by draggingRotMx and put the result in currRotMx.
Finally, in redraw(), instead of
glRotated(m_angle, m_rotAxis.x, m_rotAxis.y, m_rotAxis.z);
rotate by currRotMx.
Update: Or instead of all that... I haven't tested this, but I think it would work:
Make cur_pos a class member so it stays around, but it's initialized to zero, as is m_lastTrackPoint.
Then, whenever a new drag motion is started, before you initialize m_lastTrackPoint, let _3V dpos = cur_pos - m_lastTrackPoint (pseudocode).
Finally, when you do initialize m_lastTrackPoint based on the mouse event coords, subtract dpos from it.
That way, your cur_pos will already be offset from m_lastTrackPoint by an amount based on the accumulation of offsets from past arcball drags.
Probably error would accumulate as well, but it should be gradual enough so as not to be noticeable. But I'd want to test it to be sure... composed rotations are tricky enough that I don't trust them without seeing them.
P.S. your username is demotivating. Suggest picking another one.
P.P.S. For those who come later searching for answers to this question, the keywords to search on are "arcball rotation". An definitive article is Ken Shoemake's section in Graphical Gems IV. See also this arcball tutorial for JOGL.