mouse press projection (c++ openGL) - c++

I'm making a simple tutorial game with openGL and have a question about touch method. Please checkout my code:
My (0,0) point is in center of a screen:
void Init()
{
glClearColor(0.3,0.3,0.3,0.0);
glMatrixMode(GL_PROJECTION);
glOrtho(-400.0,400.0,-300.0,300.0,0,1.0); //сетка, середина в точке 0
}
Before this i'm call mouse methods:
glutPassiveMotionFunc(Mouse);
glutMouseFunc(MousePress);
And in method MousePress when touch is coming, it's another system coordinate with (0,0) point in top left corner of a screen. Please can you tell me better approach then make something like x-300;y-400 in MousePress method.

Given the simpler orthographic projection, your "x-300;y-400" is the correct approach, although you might want to do some scaling too...
float x = mouseX/(float)windowWidth;
float y = 1.0f - mouseY/(float)windowHeight; //flip since y=0 is at the top
//x and y are now 0 to 1, bottom left to top right
x = left + x * (right - left);
y = bottom + y * (top - bottom);
//x and y are now in 3D coordinates
Here, left/right/bottom/top are from glOrtho, which in your case can be substituted as follows (but of course storing in a variable is better)...
x = -400 + x * (400 - (-400));
y = -300 + y * (300 - (-300));
If you were using a perspective projection it gets a bit more complicated, as I've described here.
[EDIT]
Assuming the window size is 800x600, the above cancels to {x-400,300-y}. For example,
mouseY = 50;
windowHeight = 600;
float y = 1.0f - (50/(float)600); //1.0 - 0.08333 = 0.91667
y = -300 + y * (300 - (-300)); //-300 + 0.91667 * 600 = 250, also 300-50

Related

Projecting line from camera

I'm trying to convert a viewport click onto a world position for an object.
It would be quite simple if all I wanted was to draw a point exactly where the user clicks in the canvas:
void Canvas::getClickPosition(int x, int y, Vector3d(&out)[2]) const
{
Vector4d point4d[2];
Vector2d point2d(x, y);
int w = canvas.width();
int h = canvas.height();
Matrix4d model = m_world * m_camera;
for (int i = 0; i < 2; ++i) {
Vector4d sw(point2d.x() / (0.5 * w) - 1,
point2d.y() / (0.5* h) - 1, i * 1, 1);
point4d[i] = (m_proj * model).inverse() * sw;
out[i] = point4d.block<1, 3>(0, 0);
}
}
The expected behavior is achieved with this simple code.
The problem arises when I try to actually make a line that will look like a one pixel when the user first clicks it. Until the camera is rotated in any direction it should look like it was perfectly shot from the camera and that it has whatever length (doesn't matter).
I tried the obvious:
Vector4d sw(point2d.x() / (0.5 * w) - 1,
point2d.y() / (0.5* h) - 1, 1, 1); // Z is now 1 instead of 0.
The result is, as most of you guys should expect, a line that pursues the vanishing point, at the center of the screen. Therefore, the farther I click from the center, the more the line is twitched from it's expected direction.
What can I do to have a line show as a dot from the click point of view, no matter where at the screen?
EDIT: for better clarity, I'm trying to draw the lines like this:
glBegin(GL_LINES);
line.p1 = m_proj * (m_world * m_camera) * line.p1;
line.p2 = m_proj * (m_world * m_camera) * line.p2;
glVertex3f(line.p1.x(), line.p1.y(), line.p1.z());
glVertex3f(line.p2.x(), line.p2.y(), line.p2.z());
glEnd();
Your initial attempt is actually very close. The only thing you are missing is the perspective divide:
out[i] = point4d.block<1, 3>(0, 0) / point4d.w();
Depending on your projection matrix, you might also need to specify a z-value of -1 for the near plane instead of 0.
And yes, your order of matrices projection * model * view seems strange. But as long as you keep the same order in both procedures, you should get a consistent result.
Make sure that the y-axis of your window coordinate system is pointing upwards. Otherwise, you will get a result that is reflected at the horizontal midline.

Calculating a future position of a ball that can bounce off the walls

I'm trying to do some future prediction where I want to calculate where the ball should be a particular height (y). The ball is fired upwards and can bounce off the sides of the game. Bounces do not affect movement velocity.
My current config is bottom left is (0,0) and bottom right is (10,0) with no upper height limit.
Code:
void getPositionXAtHeight(float height, Vec2 pos, Vec2 vel, float gravityForce = 9.8f, float gameWidth, float& positionX)
{
float a = gravityForce / 2.0f;
float b = vel.y;
float c = pos.y - height;
float t = (sqrtf((b * b) - (4.0f * a * c)) - b) / (2.0f * a);
positionX = pos.x + (vel.x*t);
}
Can anyone advise if my code is correct so far and what I should do to handle wall bounces?
Also I feel I should do some error checking but am unsure where abouts I need to?
It's not clear what's the direction of gravity, but suppose that it's along the y-axis. Then, for symmetry, you can just "wrap around" the x values, if they go above 10. E.g. if your equation results in x=11, for symmetry, after the bounce, x=9 (10 - (11 - 10)).
Similarly, you can treat "double bounces", etc.

Sprite rotation offset doesn't stay where it belongs. (SDL)

So, here is the code for my 2D point class to rotate:
float nx = (x * cos(angle)) - (y * sin(angle));
float ny = (y * cos(angle)) + (x * sin(angle));
x = nx;
y = ny;
x and y are local variables in the point class.
And here is the code for my sprite class's rotation:
//Make clip
SDL_Rect clip;
clip.w = width;
clip.h = height;
clip.x = (width * _frameX) + (sep * (_frameX) + osX);
clip.y = (height * _frameY) + (sep * (_frameY) + osY);
//Make a rotated image
col bgColor = image->format->colorkey;
//Surfaces
img *toEdit = newImage(clip.w, clip.h);
img *toDraw = 0;
//Copy the source into the workspace
drawRect(0, 0, toEdit->w, toEdit->h, toEdit, bgColor);
drawImage(0, 0, image, toEdit, &clip);
//Edit the image
toDraw = SPG_Transform(toEdit, bgColor, angle, xScale, yScale, SPG_NONE);
SDL_SetColorKey(toDraw, SDL_SRCCOLORKEY, bgColor);
//Find new origin and offset by pivot
2DVec *pivot = new xyVec(pvX, pvY);
pivot->rotate(angle);
//Draw and remove the finished image
drawImage(_x - pivot->x - (toDraw->w / 2), _y - pivot->y - (toDraw->h / 2), toDraw, _destination);
//Delete stuff
deleteImage(toEdit);
delete pivot;
deleteImage(toDraw);
The code uses the center of the sprite as the origin. It works fine if I leave the pivot at (0,0), but if I move it somewhere else, the character's shoulder for instance, it starts making the sprite dance around as it spins like a spirograph, instead of the pivot staying on the character's shoulder.
The image rotation function is from SPriG, a library for drawing primitives and transformed images in SDL. Since the pivot is coming from the center of the image, I figure the new size of the clipped surface produced by rotating shouldn't matter.
[EDIT]
I've messed with the code a bit. By slowing it down, I found that for some reason, the vector is rotating 60 times faster than the image, even though I'm not multiplying anything by 60. So, I tried to just divide the input by 60, only now, it's coming out all jerky and not rotating to anything between multiples of 60.
The vector rotation code I found on this very site, and people have repeatedly confirmed that it works, so why does it only rotate in increments of 60?
I haven't touched the source of SPriG in a long time, but I can give you some info.
If SPriG has problems with rotating off of center, it would probably be faster and easier for you to migrate to SDL_gpu (and I suggest SDL 2.0). That way you get a similar API but the performance is much better (it uses the graphics card).
I can guess that the vector does not rotate 60 times faster than the image, but rather more like 57 times faster! This is because you are rotating the vector with sin() and cos(), which accept values in radians. The image is being rotated by an angle in degrees. The conversion factor for radians to degrees is 180/pi, which is about 57. SPriG can use either degrees or radians, but uses degrees by default. Use SPG_EnableRadians(1) to switch that behavior. Alternatively, you can stick to degree measure in your angle variable by multiplying the argument to sin() and cos() by pi/180.

How to convert from GLUT coordinates to window coordinates [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Glut Mouse Coordinates
Let's say that I have a window of 600x600.
When I receive mouse events I don't know what's the real position of the mouse, in OpenGL I use this to draw points:
(-0.5,0.5) | (0.5,0.5)
|
--------(0,0)-------
|
|
(-0.5,-0.5) | (0.5,-0.5)
But when I receive GLUT mouse events depending on the size of the window I get different coordinates.I want a relative (to the window) coordinate system.How do I get this?
I am pretty sure glut gives you the mouse coordinates in window space (i.e. if the window is 800x600 and the mouse position is in the middle, it will give you x: 400, y: 300), so if you want to bring that to the opengl space you posted above, you would do the following:
float x = (400 / 800) - 0.5f; //0.0
float y = (300 / 600) - 0.5f; //0.0
so a generic version would look something like this:
float mouseX = (theGlutMouseXCoordinate / theGlutWindowWidth) - 0.5f;
float mouseY = (theGlutMouseYCoordinate / theGlutWindowHeight) - 0.5f;
Maybe I'm misreading your question, or oversimplifying the answer, but aren't you just looking for something like:
float x = mouse.x / screen.width; //x now in [0,1]
float y = mouse.y / screen.height; //y now in [0,1]
x-=0.5f;
y-=0.5f;
Or reversed:
float wx = (x + 0.5f) * screen.width;
float wy = (Y + 0.5f) * screen.height;

How do you rotate a sprite around its center by calculating a new x and y position?

I'm using Dark GDK and C++ to create a simple 2d game. I'm rotating an object but it rotates from the top left corner of the sprite.
I have the following variables available:
PlayerX
PlayerY
PlayerWidth
PlayerHeight
RotateAngle (360 > x > 0)
Is there an algorithm that will modify the pivot point of the sprite, preferable to the center?
Here is a small code sample:
void Player::Move( void )
{
if ( checkLeft() )
{
PlayerX -= PlayerSpeed;
if ( PlayerX < 0 )
PlayerX = 0;
}
if ( checkRight() )
{
PlayerX += PlayerSpeed ;
if ( PlayerX > 800 - PlayerWidth )
PlayerX = 800 - PlayerWidth;
}
if ( checkUp())
{
PlayerY -= PlayerSpeed;
if ( PlayerY < 0 )
PlayerY = 0;
}
if ( checkDown())
{
PlayerY += PlayerSpeed;
if ( PlayerY > 600 - PlayerHeight)
PlayerY = 600 - PlayerHeight;
}
RotateAngle += 5;
if(RotateAngle > 360)
RotateAngle -=360;
dbRotateSprite(Handle,RotateAngle);
dbSprite ( 1 , PlayerX , PlayerY , Handle );
}
Edit
I'm considering opening up some reputation for this question, I have yet to be provided with an answer that works for me. If someone can provide an actual code sample that does the job, I'd be very happy.
The problem with Blindy's answer is that no matter how much I simply translate it back or forth, the spirte still rotates around the top left hand corner and moving it somewhere rotating around the top left corner, then moving it back to the same position accomplishes nothing. Here is what I believe to be going on:
alt text http://img248.imageshack.us/img248/6717/36512474.png
Just so there is no confusion I have created a an image of what is going on. The left shows what is actually happening and the right shows what I need to happen.
alt text http://img101.imageshack.us/img101/1593/36679446.png
You'd need to do something like:
translate by (-playerx-playerwidth/2, -playery-playerheight/2)
rotate by rotateangle
translate by (playerx+playerwidth/2, playery+playerheight/2)
The idea is to center your sprite on the origin then rotate around the origin (glRotate) and after you get the rotated sprite you translate it back in its place.
NB: If your sprite is initially "centered" around the origin, but with a corner not the actual center of the sprite, you first translate the object to center the sprite's center with the origin. So like if your sprite had the top-left corner in the origin, you'd translate by (-playerwidth/2, -playerheight/2), then rotate then translate by (playerx,playery).
The answers so far are correct in telling you how it should be done but I fear that the Dark GDK API seems to be too primitive to be able to do it that simple way.
Unfortunately dbRotateSprite rotates the sprite about the top left regardless of the sprite's transform which is why you're having no luck with the other suggestions. To simulate rotation about the centre you must manually correct the position of the sprite i.e. you simply have to rotate the sprite and then move it as a two-step process.
I'm not familiar with the API and I don't know if y is measured up or down and which way the angle is measured so I'm going to make some assumptions. If y is measured down like many other 2D graphics systems, and the angle is measured from the x-axis increasing as it goes from the positive x-axis to the positive y-axis, then I believe the correct psuedo-code would look like
// PlayerX and PlayerY denote the sprite centre
// RotateAngle is an absolute rotation i.e. not a relative, incremental rotation
RotateAngle += 5;
RotateAngle %= 360;
RadiansRotate = (RotateAngle * PI) / 180;
dbRotateSprite( Handle, RotateAngle );
HalfSpriteWidth = dbSpriteWidth( Handle ) / 2;
HalfSpriteHeight = dbSpriteHeight( Handle ) / 2;
SpriteX = PlayerX
- HalfSpriteWidth * cos(RadiansRotate)
+ HalfSpriteHeight * sin(RadiansRotate);
SpriteY = PlayerY
- HalfSpriteHeight * cos(RadiansRotate)
- HalfSpriteWidth * sin(RadiansRotate);
// Position the top left of the sprite at ( SpriteX, SpriteY )
dbSprite ( 1 , SpriteX , SpriteY , Handle );
When you rotate the object, you are applying a transformation to the points that compose the object. In this case, the four corners each rotate on their own, and the end result is the quad formed in their new locations. As everyone else has mentioned, the critical part of this is knowing where the origin about which the points rotate.
Imagine if instead of a sprite, you only had one point. If its origin was at the same position as that point, rotating it would have no effect (the position of the point would not move). However, if the origin was anywhere else, the point would rotate in a circle, with the origin as the center of that circle. And how would you get that origin to be somewhere other than at the same position as the point? Move the point within the coordinate system. In other words, translate it.
The order of the various transformations (rotate, translate, scale, etc.) is critical to this. If you rotated the point 180 degrees and then translated it to the right, it would end up to the right of where it started, but if you moved it to the right and then rotated it 180 degrees, it would end up on the left.
I'd recommend reading up on 2D transformations. Understanding how matrices play in all this would also be useful, but not strictly necessary to get the effect you're looking for.
Looking at your image, what you're doing is you're rotating the square in place, you're not translating it at all. This is what you're doing:
^ ^
| |
| |
| ====> |
| |
+--+-----> x---------->
| | / \
+--+ \ /
x
As you can see, your square is with its top-left corner in the origin and all you're doing is rotating it around the origin.
This is what I'm saying you should do:
^ ^ ^
| | |
| | ===> | ==> translate to
| ====> | x ==> where you want
| +-+ /|\ ==> to draw it
+--+-----> |+|---------> +-------->
| | +-+ \ /
+--+ x
You can only rotate around the center, so center the point you want to rotate your primitives around then place them where you want.
OMG ive been tryna do this for a while now to, n i found this page, just copy this.
this is based on one of the previous posts.
void dbRotateSpriteCenter(int iID, int iX, int iY, int fRotate, int iImage)
{
int x = iX
- dbSpriteWidth(iID) / 2 * cos(fRotate * 3.1415926536 / 180)
+ dbSpriteHeight(iID) / 2 * sin(fRotate * 3.1415926536 / 180);
int y = iY
- dbSpriteHeight(iID) / 2 * cos(fRotate * 3.1415926536 / 180)
- dbSpriteWidth(iID) / 2 * sin(fRotate * 3.1415926536 / 180);
dbRotateSprite(iID, fRotate);
dbSprite(iID, x, y, iImage);
}
you could use dbOffsetSprite();
when sprites are created, their insertion point is upper-left corner by default (and dbRotateSprite(); rotates them around the insertion point), you can use this function to change the insertion point. its format is dbOffsetSprite(sprite number, amount to offset X, amount to offset Y);
so you could say
dbOffsetSprite(NUMBER, dbSpriteWidth(NUMBER) / 2, dbSpriteHeight(NUMBER) / 2);
where NUMBER is the sprite ID number.
The sprite's insertion point is now in the center of its image.
Of course, this might open a whole new can of worms, as the insertion point is now in the center of the sprite (or where ever you set it), meaning calling dbSpriteX(NUMBER); will give you the center of the sprite instead of the edge.