OpenGL rotate camera on 2 keys - glfw

Basically what I'm trying to do is rotate a camera around an object in the center when I hold down "c" and use the arrow keys.
My first question doesn't have much to do with the camera, but with having a key callback recognize 2 keys at the same time.
My function DOES work if I have separate if statements for L/R keys and no "c", but I can't get it to work when I only want the camera to rotate when I'm holding down "c". I have tried using switch(key) and if statements within if statements. Both implementations I've tried are in the code below:
float R = 0.0;
float U = 0.0;
static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if(key == GLFW_KEY_C && action == GLFW_PRESS) {
switch(key) {
case GLFW_KEY_RIGHT:
R+=0.05;
camera(R, U);
break;
case GLFW_KEY_LEFT:
R-=0.05;
camera(R, U);
break;
case GLFW_KEY_UP:
break;
case GLFW_KEY_DOWN:
break;
default:
break;
}
}
//OR --
if(key == GLFW_KEY_C && action == GLFW_PRESS) {
if(key == GLFW_KEY_RIGHT && action == GLFW_PRESS) {
R+=0.05;
camera(R, U);
}
}
}
What am I doing wrong? Is there something else I can try?
My second question has more to do with the camera. It rotates fine for a half circle around the object with my current code, but then once it reaches a certain point, the camera just pans farther and farther away from the object, rather than rotating.
This is the code for my camera function:
GLfloat ox = 10.0;
GLfloat oy = 10.0;
static void camera(float RL, float UD) {
ox+=cos(glm::radians(RL));
oy+=sin(glm::radians(RL));
gViewMatrix = glm::lookAt(glm::vec3(ox, oy, 10.0f), // eye
glm::vec3(0.0, 0.0, 0.0), // center
glm::vec3(0.0, 1.0, 0.0));
}

Your 'C' detecting code won't work because you're only matching when key equals C and an arrow. It can't be both. You'll have to keep another global variable, isCPressed. When C is pressed you set it to true, when C is released you set it to false, and then when an arrow is pressed you check if(isCPressed).
as for the camera code, your algorithm orbits the point 0,0,10 while looking at 0,0,0. That doesn't seem to be what you want, you should have something like oxdist, oydist, 0 for the eye position to orbit 0,0,0 at a distance of 'dist'

Related

Top-down perspective aim tank turret with mouse

I want to make tank's turret be aimed with mouse in a top-down perspective. I have written some code to animate rotation to given angle:
void Tank::rotateTurret(float angle) {
turretRotation += angle;
}
sf::Sprite turret;
void Tank::update(unsigned int time) {
if (turretRotation != 0.0f) {
float rotate;
if (turretRotation > 0.0f) {
rotate = turretRotationSpeed * time;
if (rotate > turretRotation) {
rotate = turretRotation;
turretRotation = 0;
}
else
turretRotation -= rotate;
}
else {
rotate = -turretRotationSpeed * time;
if (rotate < turretRotation) {
rotate = turretRotation;
turretRotation = 0;
}
else
turretRotation -= rotate;
}
turret.rotate(rotate);
}
}
And I can calculate mouse pointer angle relative to top left corner:
void TankPlayerController::update() {
sf::Vector2i mousePosition = sf::Mouse::getPosition(*relativeWindow);
sf::Vector2i mouseMovement = mousePosition - lastMousePosition;
if (mouseMovement.x != 0 || mouseMovement.y != 0) {
float mouseAngle = VectorAngleDeg(mousePosition.x, mousePosition.y);
tank->rotateTurret(???);
lastMousePosition = mousePosition;
}
}
But I have no idea how to combine it together. How should it be done?
You need to calculate the angle to the center of the turret (CoT) from the upper left corner (ULHC) and the angle to the mouse location from the ULHC. Next consider the triangle formed from the lines connecting the ULHC to the CoT, the line connecting the ULHC to the mouse pointer location and the line connecting the CoT to the mouse pointer location. Since you know the distance from the ULHC to the CoT and the distance from the ULHC to the mouse pointer location all you need to do is determine the difference between the angle to the CoT and the mouse pointer position you can use the Law of Cosines to get the angel between the ULHC and the mouse position at the turret and from there the angle to any arbitrary axis you choose.
It would be easier with a picture :|

First view perspective: rotation is driving me crazy

I would like to implement a simple example in OpenGL just to test the first perspective view (without using lookAt facility), but it is driving me crazy!
I made a little prototype using Processing (that for this stuff is quite similar to OpenGL), but I got strange behavior when camera start to move!
It's not clear to me which it is the order of transformation (I have already tried all combination ;-))!
I was thinking that it should be simple and a possible solution could be:
Translate to the position of the camera
Rotate by angle of the camera
Translate each object in its position
In my simple example I positioned a box and a grid in the (0, 0, -400), but it doesn't work as expected. When I move camera along the X or Z axis the rotation around Y axis seems to rotate around a wrong center! I'd like to simulate a rotation of the camera around its own Y axis, just like a classic FPS game.
Here my sample code where user can move the camera and rotate (just around Y axis), you can test with Processing or using OpenProcessing.
Only the first few lines are relevant to the problem... So it's a very little test!
float cameraXPos = 0;
float cameraYPos = 0;
float cameraZPos = 0;
float cameraYAngle = 0;
float moveIncrement = 5;
float angleIncrement = 5;
// Keys:
// W
// ^
// |
// A <- -> D
// |
// V
// S
//
// F and R for Z+/Z-
// O and P for rotation around Y axis
void setup()
{
size(640, 480, OPENGL);
resetCameraPos();
}
// Reset camera
void resetCameraPos()
{
cameraXPos = width / 2;
cameraYPos = height / 2;
cameraZPos = (height /2 ) / tan(PI/6);
cameraYAngle = 0;
}
void draw()
{
// Clear screen
background(0);
// View transform
translate(cameraXPos, cameraYPos, cameraZPos);
rotateY(radians(cameraYAngle));
// World transform
translate(0, 0, -400);
// Draw a red box and a grid in the center
stroke(255, 0, 0);
noFill();
box(100);
drawGrid();
// Check if user is pressing some key and update the camera position
updateCameraPos();
}
/////////////////////////////////////////////////////////////////////
// The following part is not so relevant to the problem (I hope! ;-))
/////////////////////////////////////////////////////////////////////
void drawGrid()
{
// Draw a white grid (not so important thing here!)
stroke(255, 255, 255);
float cellSize = 40;
int gridSize = 10;
float cY = 100;
for(int z = 0; z < gridSize; z++)
{
float cZ = (gridSize / 2 - z) * cellSize;
for(int x = 0; x < gridSize; x++)
{
float cX = (x - gridSize / 2) * cellSize;
beginShape();
vertex(cX, cY, cZ);
vertex(cX + cellSize, cY, cZ);
vertex(cX + cellSize, cY, cZ - cellSize);
vertex(cX, cY, cZ - cellSize);
vertex(cX, cY, cZ);
endShape();
}
}
}
// Just update camera position and angle rotation
// according to the pressed key on the keyboard
void updateCameraPos()
{
if (keyPressed)
{
switch(this.key)
{
case 'w': // Y++
cameraYPos += moveIncrement;
break;
case 's': // Y--
cameraYPos -= moveIncrement;
break;
case 'a': // X--
cameraXPos += moveIncrement;
break;
case 'd': // X++
cameraXPos -= moveIncrement;
break;
case 'r': // Z++
cameraZPos += moveIncrement;
break;
case 'f': // Z--
cameraZPos -= moveIncrement;
break;
case ' ': // RESET
resetCameraPos();
break;
case 'o': // Angle++
cameraYAngle += angleIncrement;
break;
case 'p': // Angle--
cameraYAngle -= angleIncrement;
break;
}
}
}
Your comment above seems to indicate that you want your cameraYAngle to rotate the camera about its current position. If so, you want to do your camera rotation first; try this:
// View transform
rotateY(radians(cameraYAngle));
translate(cameraXPos, cameraYPos, cameraZPos);
// World transform
translate(0, 0, -400);
Note that the above does nothing to keep you looking at the origin -- that is, it doesn't do anything like lookAt. Also recall that your camera starts out looking down the Z axis...
Here's where I guess you went wrong. Even though your camera and object poses are comparable, you need to generate your "View transform" and "World transform" differently: your camera pose must be inverted to generate your "View transform".
If you're chaining raw transforms (as in your example code), this means that your camera pose transforms need to be in reverse order relative to your object transforms, as well as in the reverse direction (that is, you must negate both translation vectors and rotation angles for the camera transforms).
To be more explicit, suppose both your camera and your object have comparable pose parameters: a position vector and 3 rotations about X, Y, and Z in that order. Then, your transformation sequence might be something like:
// View transform
rotateZ(radians(-cameraZAngle));
rotateY(radians(-cameraYAngle));
rotateX(radians(-cameraXAngle));
translate(-cameraXPos, -cameraYPos, -cameraZPos);
// Model transform
translate(objectXPos, objectYPos, objectZPos);
rotateX(radians(objectXAngle));
rotateY(radians(objectYAngle));
rotateZ(radians(objectZAngle));
As a conceptual check, suppose that your camera is exactly following your object, so that all position and location variables are exactly the same for camera and object. You'd expect the same view as if they were both sitting at the origin with no rotations.
Then note that, if cameraXPos==objectXPos, cameraYPos==objectYPos and so forth, the transforms in the above transform sequence all cancel out in pairs, starting at the center -- so that the end result is the same view as if they were both sitting at the origin with no rotations.
I've tried to port the same example on C and OpenGL, and... It works!
To be precise it works after I inverted the sequence of rotation and translation (as suggested by #comingstorm and as I formerly already tried).
So I come back to Processing and try to figure out why this problem happens. I had already noticed that the default camera position is in:
(width/2, height/2, (height/2) / (PI/6))
So in my code I was moving the camera off by the same distance (in opposite direction) in order to center the camera to my objects, but it didn't work. I also try to leave the camera where it was and then move manually (using the keyboard keys) to reach my objects, but it didn't work either.
So I noticed that in the draw() method there isn't any initialization of the transformation matrix.
In all examples I've seen none does this initialization, and I'm pretty sure to have read somewhere that is automatically initialized. So I was thinking/sure that it wasn't necessary.
Anyway I've tried to put as first statement of draw() method:
resetMatrix(); // Same as glLoadIdentity in OpenGL
...And now everything is working!
(For the record I noticed the same problem also in openFramework library.)
To be honest I don't understand why they (these libraries) don't put camera in the origin and above all I was expecting that if the transformation matrix is not clear at each execution of draw method the camera should be translated automatically (summing the old matrix with the new one), so it would move fast in some direction (or spinning around Y axis).
It's not clear to me the pipeline implemented in these libraries (and I can't find a good document where it is shown), but by now it is important that this problem has been fixed.

Detecting horizontal mouse motion with Glut/OpenGL

I'm trying to detect horizontal mouse motion with OpenGL, so, when detected, execute a glutPostRedisplay(). Problem is that scene is also redrawed on vertical mouse movement.
This is the code of the registered callbacks (note mouse_inix and mouse_iniy are global (double) variables):
void mouse(int button, int state, int x, int y)
{
if (state == GLUT_DOWN) {
mouse_inix = (double)x;
mouse_iniy = (double)y;
}
}
void motion(int x, int y)
{
if (((double)x) != mouse_inix) {
angle += 20.0;
glutPostRedisplay();
}
}
Are you sure? It doesn't look like from the code you've posted that vertical mouse movement will trigger the glutPostRedisplay() call.
BUT, you've defined "horizontal mouse movement" very narrowly here. If you move the mouse up and down, you're almost sure to get a few pixels of horizontal movement. Maybe you could put a dead zone around the mouse to keep it from moving on every pixel. Something like:
void motion(int x, int y)
{
if ((abs(x - (int)mouse_inix) > 10) {
angle += 20.0;
glutPostRedisplay();
}
}
That's one thing that's going on here. Another is the use of "double". Since glut is returning mouse coordinates as ints, you're better off sticking with that. Trying to compare "(double)x != mouse_inix" will almost certainly be true because of the precision issues with doubles. You generally don't want to compare for exactly equal to or not equal to using floating point numbers. The use of the dead zone will negate that issue, but still, why convert to doubles if you don't need them?
I don't know if "20" is degrees or radians, but it could result in some pretty jumpy moves either way. Consider scaling the size of the move to the size of the mouse move:
void motion(int x, int y)
{
int deltaX = (abs(x - (int)mouse_inix);
if (deltaX > 10) {
angle += (double)deltaX; // or "deltaX/scaleFactor" to make it move more/less
glutPostRedisplay();
}
}
It will still only rotate one way. If you used the sign of "deltaX", you would be able to rotated both directions depending on how you moved the mouse.

OpenGl project with drawPrimitive()

I am trying to change a C++ project, which is currently drawing some lines when I click on the view port. This functionality is perfectly fine, but what I am trying to change is when I click on "UP" or "Down" keys the color for next lines to change. Currently if I click on those keys the color changes for all the lines including the old ones (already drawn).
Please give me an idea of what to do. Here is some of the code:
void drawPrimitive() {
Vertex *temp;
// Set the primitive color
glColor3fv(primitiveColor);
// Set the point size in case we are drawing a point
if (type == POINT)
glPointSize(pointSize);
// Display results depending on the mode
glBegin(mode);
for(temp = head; temp != NULL; temp = temp->np)
{
if (smoothShading)
glColor3f(temp->r, temp->g, temp->b);
glVertex2f(temp->x, temp->y);
}
glEnd(); }
void mouse(int button, int state, int x, int y) {
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
float pointX, pointY;
pointX = (float)x/window_width * world_width;
pointY = (float)(window_height - y)/window_height * world_height;
// Add a vertex to the list of vertices...
addVertex(&head, &tail, pointX, pointY, 0.0f, primitiveColor[0], primitiveColor[1], primitiveColor[2]);
// automatically calls the display function
glutPostRedisplay();
}
else if(button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN)
{
deleteVertex(&head, &tail);
glutPostRedisplay();
} }
void special(int key, int x, int y) {
switch (key)
{
// change primitive color
case GLUT_KEY_UP :
changePrimitiveColor(1);
break;
case GLUT_KEY_DOWN :
changePrimitiveColor(-1);
break;
}
glutPostRedisplay(); }
void changePrimitiveColor(int step) {
primitiveColorId += step;
if (primitiveColorId < 0)
primitiveColorId = COLOR_COUNT - 1;
if (primitiveColorId >= COLOR_COUNT)
primitiveColorId = 0;
setColor(primitiveColor, primitiveColorId); }
Your code is sort of unclear; is primitiveColor a global variable?
Assuming you're calling drawPrimitive() for all your lines each redraw, the same primitiveColor would be used for all the lines. As soon as you change the color when you press up or down, the redisplay function is called, and all the lines will be redrawn using the same color.
What you might want to do is have a list containing both the primitives and their respective colors. When you iterate through this list, you can set a color for each line.
Keep in mind that OpenGL behaves like a statemachine. If you set a color, everything you draw after it will be drawn with that color. OpenGL will not remember that your other elements had a different color and you want to keep it like that. You have to do the bookkeeping.
So each time your content is drawn, you will have to explicitly state which elements have which color.

directx c++ first person camera

im creating a 3d room which you can walk around with a first person camera
i have defined the position of the eyeX eyeY and eyeZ as shown below:
float eyeX = 0;
float eyeY = 100;
float eyeZ = 75;
here is my lookat code:
D3DXMatrixLookAtLH( &g_matView, &D3DXVECTOR3( eyeX, eyeY,eyeZ ),
&D3DXVECTOR3( LookatX, LookatY, LookatZ ),
&D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
g_pd3dDevice->SetTransform( D3DTS_VIEW, &g_matView );
my code allows me to move the camera around but not like a first person camera and i am struggling to achieve this.
// forwards = UP ARROW
// Backwards = DOWN ARROW
// rotate left = LEFT ARROW
// rotate right = RIGHT ARROW
case WM_KEYDOWN:
{
// Handle any non-accelerated key commands
switch (wparam)
{
case VK_RIGHT:
if(eyeX >=-50)
{
--eyeX;
}
return (0);
case VK_LEFT:
if(eyeX <=50)
{
++eyeX;
}
return (0);
case VK_DOWN:
if(eyeZ >=-50)
{
--eyeZ;
}
return (0);
case VK_UP:
if(eyeZ <=50)
{
++eyeZ;
}
return (0);
case VK_SPACE:
if(eyeY >=-50)
{
--eyeY;
}
return (0);
case VK_SHIFT:
if(eyeY <=50)
{
++eyeY;
}
return (0);
}
break;
}
LookatX = eyeX + 5.0f;
LookatY = eyeY;
LookatZ = eyeZ;
case WM_DESTROY:
{
// kill the application
PostQuitMessage(0);
return(0);
}
default:
break;
} // end switch
could anyone suggest some changes which would allow me to move around my room like a first person camera?
Instead of using D3DXMatrixLookAtLH, you could keep a view matrix.
Set up
(Note that I am making up names of functions, you might have to create these yourself)
Start with something like
Matrix view = Matrices.createIdentity();
Then every frame, you set the view matrix(just like you are doing with the matrix you are getting from MatrixLookAtLH)
Moving around
Normally modifing a model matrix is like this.
model = Matrix.multiply(model,transformation).
However, you manipulate the camera backwards
view = Matrix.multiply(transformation, view)
Simply run your switch statement, generate a transformation and update the view matrix.
e.g:
if (key == 'w')
view = Matrix.multiply(Matrices.createTranslate(0,0,-5), view);
if (key = 'j') // Key to turn
view = Matrix.multiply(Matrices.createRotateY(.1), view);
Formulas for genereating these matrices can be found on wikipedia(or DirectX might give them on its own).
(This is all based off of a simple software renderer I made a while ago, but it should apply the same to DirectX)
EDIT:
Oh, it looks like DirectX has all of these functions for you already in http://msdn.microsoft.com/en-us/library/windows/desktop/bb281696(v=vs.85).aspx