First view perspective: rotation is driving me crazy - opengl

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.

Related

Drawing a sprite on the circumference of a circle based on the position of other objects

I'm making a sniper shooter arcade style game in Gamemaker Studio 2 and I want the position of targets outside of the viewport to be pointed to by chevrons that move along the circumference of the scope when it moves. I am using trig techniques to determine the coordinates but the chevron is jumping around and doesn't seem to be pointing to the target. I have the code broken into two: the code to determine the coordinates in the step event of the enemies class (the objects that will be pointed to) and a draw event in the same class. Additionally, when I try to rotate the chevron so it also points to the enemy, it doesn't draw at all.
Here's the coordinate algorithm and the code to draw the chevrons, respectively
//determine the angle the target makes with the player
delta_x = abs(ObjectPlayer.x - x); //x axis displacement
delta_y = abs(ObjectPlayer.y - y); //y axis displacement
angle = arctan2(delta_y,delta_x); //angle in radians
angle *= 180/pi //angle in radians
//Determine the direction based on the larger dimension and
largest_distance = max(x,y);
plusOrMinus = (largest_distance == x)?
sign(ObjectPlayer.x-x) : sign(ObjectPlayer.y-y);
//define the chevron coordinates
chevron_x = ObjectPlayer.x + plusOrMinus*(cos(angle) + 20);
chevron_y = ObjectPlayer.y + plusOrMinus*(sign(angle) + 20);
The drawing code
if(object_exists(ObjectEnemy)){
draw_text(ObjectPlayer.x, ObjectPlayer.y-10,string(angle));
draw_sprite(Spr_Chevron,-1,chevron_x,chevron_y);
//sSpr_Chevron.image_angle = angle;
}
Your current code is slightly more complex that it needs to be for this, if you want to draw chevrons pointing towards all enemies, you might as well do that on spot in Draw. And use degree-based functions if you're going to need degrees for drawing anyway
var px = ObjectPlayer.x;
var py = ObjectPlayer.y;
with (ObjectEnemy) {
var angle = point_direction(px, py, x, y);
var chevron_x = px + lengthdir_x(20, angle);
var chevron_y = py + lengthdir_y(20, angle);
draw_sprite_ext(Spr_Chevron, -1, chevron_x, chevron_y, 1, 1, angle, c_white, 1);
}
(also see: an almost-decade old blog post of mine about doing this while clamping to screen edges instead)
Specific problems with your existing code are:
Using a single-axis plusOrMinus with two axes
Adding 20 to sine/cosine instead of multiplying them by it
Trying to apply an angle to sSpr_Chevron (?) instead of using draw_sprite_ext to draw a rotated sprite.
Calculating largest_distance based on executing instance's X/Y instead of delta X/Y.

How to rotate an object so it faces another?

I am making a game in opengl, and i can't figure out how to make my enemy characters turn to face my player. I only need the enemy to rotate on the y axis towards the player. Then I want them to move towards him.I have tried a bunch of different methods but haven't been able to get anything to work.
There are a few things you need to decide on yourself at the beginning of the project to be used throughout the project, like the representation of positions and the orientation (as well as the setup of the screen/clip planes etc.) However, you haven't mentioned any of this. So you may have to adapt the code below to suit your game, but it should be easily adaptable and applicable.
For the following example, I'll assume that -y axis is the top of your screen.
#include <math.h> // atan2
// you need to way to represent position and directions
struct vector2{
float x;
float y;
} playerPosition, enemyPosition;
float playerRotation;
// setup the instances and values
void setup() {
// Set some default values for the positions
playerPosition.x = 100;
playerPosition.y = 100;
enemyPosition.x = 200;
enemyPosition.y = 300;
}
// called every frame
void update(float delta){
// get the direction vector between the player and the enemy. We can then use this to both calculate the rotation angle between the two as well as move the player towards the enemy.
vector2 dirToEnemy;
dirToEnemy.x = playerPosition.x - enemyPosition.x;
dirToEnemy.y = playerPosition.y - enemyPosition.y;
// move the player towards the enemy
playerPosition.x += dirToEnemy.x * delta * MOVEMENT_SPEED;
playerPosition.y += dirToEnemy.y * delta * MOVEMENT_SPEED;
// get the player angle on the y axis
playerRotation = atan2(-dirToEnemy.y, dirToEnemy.x);
}
void draw(){
// use the playerPosition and playerAngle to render the player
}
Using the above code, you should be able to move your player object around and set the angle of rotation (you need to watch out for radians/degrees of the returned and expected angle values).

How to define a path of motion for the camera in OpenGL?

EDIT: I CHANGED THE QUESTION WHEN I REALIZED THAT I ACTUALLY NEEDED TO MOVE THE CAMERA AND NOT THE POLYGON. MY BAD, EVERYONE.
So I'm taking a class on OpenGL and need to define a path that the camera can automatically traverse.
How can I make my camera move around my world by hitting the F1 key?
Just to clarify,
I know how to use keyboard functions that use constants such as GLUT_KEY_F1.
I'm trying something new and am clueless.
Is this as simple as incrementing (with a loop) the values in the parameters of gluLookAt() ?
Here is the code that I have now:
if(key == GLUT_KEY_F1)
{
float x;
collisionKey = GLUT_KEY_F1;
for(x = 0.0; x < 3600.0;)
{
x += .01;
gluLookAt(200 + x, cameraY, 400 + cameraZ, 200 + studentX, 250 + studentY, studentZ, 0, 1, 0);
}
}
glutPostRedisplay();
Let me know if you have any questions!

OpenGL Frustum visibility test with sphere : Far plane not working

I am doing a program to test sphere-frustum intersection and being able to determine the sphere's visibility. I am extracting the frustum's clipping planes into camera space and checking for intersection. It works perfectly for all planes except the far plane and I cannot figure out why. I keep pulling the camera back but my program still claims the sphere is visible, despite it having been clipped long ago. If I go far enough it eventually determines that it is not visible, but this is some distance after it has exited the frustum.
I am using a unit sphere at the origin for the test. I am using the OpenGL Mathematics (GLM) library for vector and matrix data structures and for its built in math functions. Here is my code for the visibility function:
void visibilityTest(const struct MVP *mvp) {
static bool visLastTime = true;
bool visThisTime;
const glm::vec4 modelCenter_worldSpace = glm::vec4(0,0,0,1); //at origin
const int negRadius = -1; //unit sphere
//Get cam space model center
glm::vec4 modelCenter_cameraSpace = mvp->view * mvp->model * modelCenter_worldSpace;
//---------Get Frustum Planes--------
//extract projection matrix row vectors
//NOTE: since glm stores their mats in column-major order, we extract columns
glm::vec4 rowVec[4];
for(int i = 0; i < 4; i++) {
rowVec[i] = glm::vec4( mvp->projection[0][i], mvp->projection[1][i], mvp->projection[2][i], mvp->projection[3][i] );
}
//determine frustum clipping planes (in camera space)
glm::vec4 plane[6];
//NOTE: recall that indices start at zero. So M4 + M3 will be rowVec[3] + rowVec[2]
plane[0] = rowVec[3] + rowVec[2]; //near
plane[1] = rowVec[3] - rowVec[2]; //far
plane[2] = rowVec[3] + rowVec[0]; //left
plane[3] = rowVec[3] - rowVec[0]; //right
plane[4] = rowVec[3] + rowVec[1]; //bottom
plane[5] = rowVec[3] - rowVec[1]; //top
//extend view frustum by 1 all directions; near/far along local z, left/right among local x, bottom/top along local y
// -Ax' -By' -Cz' + D = D'
plane[0][3] -= plane[0][2]; // <x',y',z'> = <0,0,1>
plane[1][3] += plane[1][2]; // <0,0,-1>
plane[2][3] += plane[2][0]; // <-1,0,0>
plane[3][3] -= plane[3][0]; // <1,0,0>
plane[4][3] += plane[4][1]; // <0,-1,0>
plane[5][3] -= plane[5][1]; // <0,1,0>
//----------Determine Frustum-Sphere intersection--------
//if any of the dot products between model center and frustum plane is less than -r, then the object falls outside the view frustum
visThisTime = true;
for(int i = 0; i < 6; i++) {
if( glm::dot(plane[i], modelCenter_cameraSpace) < static_cast<float>(negRadius) ) {
visThisTime = false;
}
}
if(visThisTime != visLastTime) {
printf("Sphere is %s visible\n", (visThisTime) ? "" : "NOT " );
visLastTime = visThisTime;
}
}
The polygons appear to be clipped by the far plane properly so it seems that the projection matrix is set up properly, but the calculations make it seem like the plane is way far out. Perhaps I am not calculating something correctly or have a fundamental misunderstanding of the calculations that are required?
The calculations that deal specifically with the far clipping plane are:
plane[1] = rowVec[3] - rowVec[2]; //far
and
plane[1][3] += plane[1][2]; // <0,0,-1>
I'm setting the plane to be equal to the 4th row (or in this case column) of the projection matrix - the 3rd row of the projection matrix. Then I'm extending the far plane one unit further (due to the sphere's radius of one; D' = D - C(-1) )
I've looked over this code many times and I can't see why it shouldn't work. Any help is appreciated.
EDIT:
I can't answer my own question as I don't have the rep, so I will post it here.
The problem was that I wasn't normalizing the plane equations. This didn't seem to make much of a difference for any of the clip planes besides the far one, so I hadn't even considered it (but that didn't make it any less wrong). After normalization everything works properly.

How do I make projectiles?

I am totally stumped on this one. I'm using C++ and SFML 1.6 for a game I'm developing, and I have no bloody idea. How do I make projectiles (like bullets)? I just don't understand it. It could be my lack of sleep but I don't know.
So my question is how do I create a Sprite that moves in a definite direction based on where the mouse is? (Think of a top down shooter with mouse aiming)
Easiest solution:
If the mouse is at Mx,My and the ship is at Sx,Sy then calculate the direction from the ship to the mouse:
Dx=Sx-Mx
Dy=Sy-My
Now normalise D (this means scale it so that it's length is one):
DLen=sqrt(Dx*Dx + Dy*Dy)
Dx/=DLen;
Dy/=DLen;
Now Dx is the distance you want to move the bullet on the x axis in order to get bullet speed of 1.
Thus each frame you move the bullet like so (position of bullet: Bx,By Speed of bullet: Bs [in pixels per millisec] Frame time Ft[in millisec])
Bx=Bx+Dx*Bs*Ft
By=By+Dy*Bs*Ft
This give you a bullet that moves towards the mouse position at a speed independent of the direction of the mouse or framerate of the game.
EDIT: As #MSalters says you need to check for the DLen==0 case when the mouse is directly above the ship to avoid division by zero errors on the normalise
One way to do it is to make the bullet face the mouse and then move it across the x and y axis by using trigonometry to find the hypotinuse from the angle. I don't think i explained this very well, so here the code to make a sprite move from its rotation:
void sMove(sf::Sprite& input,float toMove, int rotation){
bool negY = false;
bool negX = false;
int newRot = rotation;
if (rotation <= 90){
negY = true;
}
else if (rotation <= 180){
//newRot -= 90;
negY = true;
}
else if (rotation <= 360){
newRot -= 270;
negY = true;
negX = true;
}
float y = toMove*cos(newRot*PI/180);
float x = toMove*sin(newRot*PI/180);
if (negY){
y = y*-1;
}
if (negX){
x = x*-1
}
input.move(x, y);
}