Project an object with rotation around another object in C++/OpenGL - c++

I'm Using OpenGL/C++ to create a game.
One aspect of this game that I'm working on is having a character that shoots a projectile the way said character is facing. To do this I have a 'player' and a 'projectile'.
I pass to the projectile the characters x and y co-ordinates, the angle the player is facing. From this I want to shoot the projectile in that direction.
In my draw I am currently using glTranslate with the characters x and y and rotating the projectile on the way the character is facing. This moves my projectile to the way the player is facing.
glTranslatef(this->m_X, this->m_Y, 0);
glRotatef(angle, 0, 0, 1);
This is where i'm stuck, I can move the projectile position by incrementing/decrementing the X and Y values in the translate. But what I'm trying to ask is how can I move the projectile along the line the player is facing.
Thanks for the help!

You can use polar vectors for these calculations.
http://mathworld.wolfram.com/PolarVector.html
A polar vector will allow you to make several calculations that would normally be complicated and convoluted in a simple way. Using their applied mathematics your request won't be an issue.
Here's an implementation of mine of polar vectors.
The header file:
#include <cmath>
//Using SFML Vector2 class, making a similar class is easy.
//Check this URL: http://www.sfml-dev.org/documentation/2.3.2/classsf_1_1Vector2.php
class PolarVector
{
public:
float r;
float t; ///Angle stored in degrees.
PolarVector();
PolarVector(float radius, float angle);
PolarVector(const sf::Vector2f V2); ///Conversion constructor.
sf::Vector2f TurnToRectangular() const;
};
PolarVector TurnToPolar(const sf::Vector2f point);
float getConvertedRadius(const sf::Vector2f point);
float getConvertedAngle(sf::Vector2f point);
bool operator ==(const PolarVector& left, const PolarVector& right);
bool operator !=(const PolarVector& left, const PolarVector& right);
And the source file:
#include "PolarVector.hpp"
PolarVector::PolarVector()
:r(0.f)
,t(0.f)
{}
PolarVector::PolarVector(float radius, float angle)
:r(radius)
,t(angle)
{}
PolarVector::PolarVector(const sf::Vector2f V2)
:r(getConvertedRadius(V2))
,t(getConvertedAngle(V2))
{}
sf::Vector2f PolarVector::TurnToRectangular() const
{ return sf::Vector2f(static_cast<float>(r* std::cos(t)), static_cast<float>(r* std::sin(t))); }
PolarVector TurnToPolar(const sf::Vector2f point)
{
PolarVector PV;
PV.r = getConvertedAngle(point);
PV.t = getConvertedRadius(point);
return PV;
}
float getConvertedRadius(const sf::Vector2f point)
{ return std::sqrt((point.x * point.x) + (point.y * point.y) ); }
float getConvertedAngle(const sf::Vector2f point)
{ return std::atan2(point.y, point.x); }
bool operator ==(const PolarVector& left, const PolarVector& right)
{
float diffR = left.r - right.r;
float diffA = left.t - right.t;
return ((diffR <= EPSILON) && (diffA <= EPSILON));
}
bool operator !=(const PolarVector& left, const PolarVector& right)
{
float diffR = left.r - right.r;
float diffA = left.t - right.t;
return !((diffR <= EPSILON) && (diffA <= EPSILON));
}
The reason why I suggest this is because you can do the following.
Let's say you have a 2 dimensional vector:
sf::Vector2f character(0.f, 0.f); //Origin point. First parameter is X, second is Y
float angleCharFacesAt = 0.698132; //40 degrees in radians. C++ Trigonometry uses Radians. std::cos, std::sin and std::atan2 are used internally.
For the first object or character. You want the other object to have the same angle, but a different position.
Let's say the other object has a position above it:
sf::Vector2f object(0.f, 10.f); //Above the origin point.
float angleObjectFacesAt = 0.f; //0 degrees.
So all you need to do is rotate it using a polar vector:
PolarVector PV = TurnToPolar(object); //Use this for calculations.
PV.t += angleCharFacesAt; //t is the angle parameter of the polar vector.
object = PV.TurnToRectangular(object);
By doing this you will get the rotated position of the object.
The distance between one object and the other will always be the r (Radius) value of the polar vector. So you could make the distance longer or shorter by doing this:
PolarVector PV = TurnToPolar(object); //Use this for calculations.
PV.r += 10; //Increase the radius to increase the distance between the objects.
object = PV.TurnToRectangular(object);
You should try to understand the rotation matrix and polar math to be able to achieve more things with this, but with this code it is possible. You should also put all this code in a class, but first play with it until you understand it well.
Sorry for the lengthy answer, but this is a topic that isn't very easy to explain without delving into linear algebra. The classes are for actual code manageability (I use these in my own game), but you can reproduce the same effects with the calculations only.
I personally prefer Polar Vectors over using the rotation matrix due to their usefulness in more than just rotating an object. But here's a link to understanding the rotation matrix better: https://en.wikipedia.org/wiki/Rotation_matrix
After you've done the transformation with the polar vector, you can just glTranslate to the final position given by the polar vector. You have to make sure that you rotate around the origin you are using. Otherwise rotation might not occur as you desire to use it.

Related

Problems rotating objects using eulers - quaternions

I'm having problems rotating GameObjects in my engine. I'm trying to rotate in 2 ways.
I'm using MathGeoLib to calculate maths in the engine.
First way: Rotates correctly around axis but if I want to rotate back, if I don't do it following the inverse order then rotation doesn't work properly.
e.g:
Rotate X axis 50 degrees, Rotate Y axis 30 degrees -> Rotate Y axis -50 degrees, Rotate X axis -30 degrees. Works.
Rotate X axis 50 degrees, Rotate Y axis 30 degrees -> Rotate X axis -50 degrees, Rotate Y axis -30 degrees. Doesn't.
Code:
void ComponentTransform::SetRotation(float3 euler_rotation)
{
float3 diff = euler_rotation - editor_rotation;
editor_rotation = euler_rotation;
math::Quat mod = math::Quat::FromEulerXYZ(diff.x * DEGTORAD, diff.y * DEGTORAD, diff.z * DEGTORAD);
quat_rotation = quat_rotation * mod;
UpdateMatrix();
}
Second way: Starts rotating good around axis but after rotating some times, then it stops to rotate correctly around axis, but if I rotate it back regardless of the rotation order it works, not like the first way.
Code:
void ComponentTransform::SetRotation(float3 euler_rotation)
{
editor_rotation = euler_rotation;
quat_rotation = math::Quat::FromEulerXYZ(euler_rotation.x * DEGTORAD, euler_rotation.y * DEGTORAD, euler_rotation.z * DEGTORAD);
UpdateMatrix();
}
Rest of code:
#define DEGTORAD 0.0174532925199432957f
void ComponentTransform::UpdateMatrix()
{
if (!this->GetGameObject()->IsParent())
{
//Get parent transform component
ComponentTransform* parent_transform = (ComponentTransform*)this->GetGameObject()->GetParent()->GetComponent(Component::CompTransform);
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);
//Multiply the object transform by parent transform
transform_matrix = parent_transform->transform_matrix * transform_matrix;
//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
else
{
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);
//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
}
MathGeoLib:
Quat MUST_USE_RESULT Quat::FromEulerXYZ(float x, float y, float z) { return (Quat::RotateX(x) * Quat::RotateY(y) * Quat::RotateZ(z)).Normalized(); }
Quat MUST_USE_RESULT Quat::RotateX(float angle)
{
return Quat(float3(1,0,0), angle);
}
Quat MUST_USE_RESULT Quat::RotateY(float angle)
{
return Quat(float3(0,1,0), angle);
}
Quat MUST_USE_RESULT Quat::RotateZ(float angle)
{
return Quat(float3(0,0,1), angle);
}
Quat(const float3 &rotationAxis, float rotationAngleRadians) { SetFromAxisAngle(rotationAxis, rotationAngleRadians); }
void Quat::SetFromAxisAngle(const float3 &axis, float angle)
{
assume1(axis.IsNormalized(), axis);
assume1(MATH_NS::IsFinite(angle), angle);
float sinz, cosz;
SinCos(angle*0.5f, sinz, cosz);
x = axis.x * sinz;
y = axis.y * sinz;
z = axis.z * sinz;
w = cosz;
}
Any help?
Thanks.
Using Euler angles and or Quaternions adds some limitations as it creates singularities which if not handled correctly will make silly things. Sadly almost all new 3D games using it wrongly. You can detect those by the well known things like:
sometimes your view get to very different angle that should not be there
object can not rotate anymore in some direction
object start rotating around different axises than it should
view jumps around singularity pole
view is spinning or flipping until you move/turn again (not the one caused by optic mouse error)
I am using cumulative transform matrices instead:
Understanding 4x4 homogenous transform matrices
Read the whole stuff (especially difference between local and global rotations) then in last 3 links you got C++ examples of how to do this (also read all 3 especially the preserving accuracy ...).
The idea is to have matrix representing your object coordinate system. And when ever you rotate (by mouse, keyboard, NAV,AI,...) you rotate the matrix (incrementally). The same goes for movement. This way they are no limitations or singularities. But also this approach has its problems:
lose of accuracy with time (read the preserving accuracy example to deal with this)
no knowledge about the Euler angles (the angles can be computed from the matrix however)
Both are solvable relatively easily.
Now when you are rotating around local axises you need to take into account that with every rotation around some axis you change the other two. So if you want to get to the original state you need to reverse order of rotations because:
rotate around x by 30deg
rotate around y by 40deg
is not the same as:
rotate around y by 40deg
rotate around x by 30deg
With cumulative matrix if you want to get back you can either iteratively drive your ship until it faces desired directions or remember original matrix and compute the rotations needed to be done one axis at a time. Or convert the matrix difference into quaternion and iterate that single rotation...

The collision in SFML is not that good, how to improve it?

I've been lately working on a simple game using C++ and SFML latest version, but I had a problem which is that the collision detection is not that good, for example the player dies even if the enemy didn't touch him yet, but just near him. Here is the code of the player class with the move function and collision detection code AND the moves of the enemy class:
`class PlayerA : public CircleShape
{
public:
//Constructor:
PlayerA(float xposition, float yposition, float radius, float s)
{
setRadius(radius);
setFillColor(Color::Yellow);
setOutlineColor(Color(00,80,00));
setOutlineThickness(-2);
setPointCount(3);
setSpeed(s);
setPosition(xposition,yposition);
}
//Movements of the player:
void up()
{
move(0,-10*speed);
}
void down()
{
move(0,10*speed);
}
void right()
{
move(10*speed,0);
}
void left()
{
move(-10*speed,0);
}
void checkA(ObsA *obs1=NULL,ObsA *obs2=NULL, ObsA *obs3=NULL, ObsA *obs4=NULL, ObsA *obs5=NULL)
{
if(obs2==NULL)
{
if(getGlobalBounds().intersects(obs1->getGlobalBounds()))
{
relevel();
}
}
private:
float speed=0.00;
void obs()
{
if(speed > 0)
{
rotate(0.5*speed);
}
else
{
rotate(0.5*speed);
}
}
private:
float speed = 0.00;
void obs()
{
if(speed > 0)
{
rotate(0.5*speed);
}
else
{
rotate(0.5*speed);
}
}
private:
float speed = 0.00;
Is there something wrong with the code, how to fix the problem, thank you!
The intersects function just check if two rectangles intersect. If you want pixel perfect collision detection in SFML you have to write that yourself.
Basically, start with intersects, if it is true, then get the intersecting rectangle and check if any pixels therein from both original rectangles contains overlaping relevant pixels.
You can use this function to perform better collision detection.Its a basic one but works well
bool circleTest(const sf::Sprite &first, const sf::Sprite &second)
{
sf::Vector2f firstRect(first.getTextureRect().width, first.getTextureRect().height);
firstRect.x *= first.getScale().x;
firstRect.y *= first.getScale().y;
sf::Vector2f secondRect(second.getTextureRect().width, second.getTextureRect().height);
secondRect.x *= second.getScale().x;
secondRect.y *= second.getScale().y;
float r1 = (firstRect.x + firstRect.y) / 4;
float r2 = (secondRect.x + secondRect.y) / 4;
float xd = first.getPosition().x - second.getPosition().x;
float yd = first.getPosition().y - second.getPosition().y;
return std::sqrt(xd * xd + yd * yd) <= r1 + r2;
}
Are you using a circle? If I remember correctly, the circle will have a rectangle hitbox. If that is the case, then you may have collision between the invisible rectangle corners.
If you're using a circle, Perhaps change class to a square rectangle and see if collision works correctly. Or try testing collision directly on an x or y axis with your circles; i.e. having them moving in a straight line towards each other only changing 1 axis. (the edge of the circle will be the same as the edge of the rectangle at the left, right, top, and bottom sections).
If you're needing a better collision for circles, there may be one already built in SFML. But I don't think it would be too much to write your own logic using the radius of your two circles, the center of your two objects, and the angle hypotenuse between the centers.
edit based on Merlyn Morgan-Graham's comment.

Circle - Axis Alined Rectangle Intersection

I want to create a function which takes a rectangle and a circle returns a boolean as to whether or not they intersect. What's the most efficient and simple way of doing this? The function looks something like this:
bool intersect(rectX, rectY, rectWidth, rectHeight, circleX, circleY, radius)
{
bool intersect;
//code I need
return intersect;
}
Please help me find the code I need. Thanks!
Your answer is at last, but I also want to inform you/future viewer, the following.
When you are denoting a rectangle by one point AND height-width that is not specific rectangle on axis-system, that is general form of a rectangle can be draw anywhere.
So, In your case: Rectangle can be draw in any direction(to make it away from circle, eg: UP,LEFT,RIGHT,BOTTOM etc. side to rectX,rectY). That's why here two possibilities generates AND your possibility will depends on what you need:
[A] 100% assurance of intersection, no matter how you draw rectangle
[b] At least one way to draw the rectangle so that it will intersect with circle
Case A:
bool assuredIntersect(rectX, rectY, rectWidth, rectHeight, circleX, circleY, radius){
bool intersect;
float distance=((rectX-circleX)^2+(rectY-circleY)^2)^0.5;
intersect=(radious>=distance);
return intersect;
}
Case B:
bool canIntersect(rectX, rectY, rectWidth, rectHeight, circleX, circleY, radius){
bool intersect;
float distance=((rectX-circleX)^2+(rectY-circleY)^2)^0.5;
float diagonal=((rectX+rectHeight)^2+(rectY-rectWidth)^2)^0.5;
intersect=((radious+diagonal)<distance);
return intersect;
}
But, if you are denoting rectangle by two-points-with-one-side (rectX1,rectY1 AND rectX2,rectY2 AND height or width). Then you can denote a rectangle specifically.
Note: If direction of rectangle is fixed(like shorter-side-from-point(rectX,rectY) is perpendicular-or-at-an-angle to x-axis) then also rectangle become specific as we can calculate (rectX2,rectY2). Eg: if angle is 90 then second point will be (rectX+rectHeight, rectY+rectWidth).
If we have function parameter like this:
#include <math.h>
#define PI 3.14159265
bool intersect(rX1, rY1, rX2, rY2, rAngle, cX, cY, cR){
//can be `intersect(rX1,rY1,rH,rW,rAngle, cX,cY, cR)`, and calculate rX2,rY2
//can be `intersect(rX1,rY1,rX2,rY2,rH, cX,cY, cR)`, and calculate rAngle
bool intersect;
//assume (rX1,rY1) as origin AND rectangle`s-side attached to this point is on both axis,
//THEN we need to recalculate coordinates according to this assumption
rAngle=rAngle*PI/180; //angle in radian
//NOTE: if in place of rAngle, rHeight or rWidth is given then you can calculate rAngle by trigonometry.
//moving rX1,xY1 to (0,0)
cX=cX-rX1; cY=cY-rY1;
rX2-=rX1; rY2-=rY1; rX1=rY1=0;
//rotating axis, rectangle, circle...
float cosA=cos(rAngle), sinA=sin(rAngle);
float tempX= cosA*rX2 + sinA*rY2;
float tempY= sinA*rX2 + cosA*rY2;
rX2=tempX; rY2=tempY;
tempX=cosA*cX + sinA*cY;
tempY=sinA*cX + cosA*cY;
cX=tempX; cY=tempY;
rX1-=cR;rY1-=cR; //enlarging(creating) virtual rectangle around original; After this...
rX2+=cR;rY2+=cR; //...if circle centre is inside this rectangle it will intersect with original rectangle
intersect=(cX<=rX2 && cX>=rX1 && cY<=rY2 && cY>=rY1);
return intersect;
}
point(x,y) rotation
# So, if you don't know angle then you can consider first two case.
[ANSWER] If rectangle is axis aligned then function will be:
bool intersect(rectX, rectY, rectWidth, rectHeight, circleX, circleY, radius){
bool intersect;
//calculating rX2,xY2
rX2=rectX + rectWidth; rY2=rectY + rectHeight;
rectX-=radius;rectY-=radius; //enlarging(creating) virtual rectangle around original; After this...
rX2+=radius;rY2+=radius; //...if circle centre is inside this rectangle it will intersect with original rectangle ...
intersect=(circleX<=rX2 && circleX>=rectX && circleY<=rY2 && circleY>=rectY);
return intersect;
}

FPS-style Camera

I'm new to GLM and trying to write a fps-style camera class
should I need to recalculate the up vector when I move the mouse?
however everything look strange after i change the up vector
Still, my program looks buggy when i maintain the up vector
file: fpscamera.cpp
void FPSCamera::rotateScreen(float dx, float dy){
auto oldDirection=getTarget()-getPosition();
setTarget(getPosition()+rotate(oldDirection, -dx*5, getUp()));
auto oldHoro=rotate(cross(oldDirection,getUp()), -dx*5, getUp());
setTarget(getPosition()+rotate(getTarget()-getPosition(),dy*5, oldHoro));
////////////////HERE////////////////////////
//setUp(normalize(cross(oldHoro,getTarget()-getPosition())));
}
You can download the source code and binary here
https://docs.google.com/file/d/0B9givuJvSet8ekRReWtRM29ldzg/edit?usp=sharing
mingw-built64 g++4.8(C++11) glfw3 GLM(MathHelper Library) required
g++ -std=c++0x -Wall main.cpp mesh.cpp fpscamera.cpp -lglfw3 -lopengl32 -lglu32 -lgdi32
move toward the target... W
turn Left/Right.... Q/E
move left/Right.... A/D
====================2013/8/6 edit
I edit my camera class and add yaw pitch roll function
void Camera::roll(float among){
if(isChange) resetCacheAxis();
upVec=glm::rotate(upVec, among, cacheY);
isChange=true;
}
void Camera::pitch(float among){
if(isChange) resetCacheAxis();
targetVec=posVec+glm::rotate(targetVec-posVec, among, cacheX);
upVec=glm::normalize(glm::cross(cacheX, targetVec-posVec));
isChange=true;
}
void Camera::yaw(float among){
if(isChange) resetCacheAxis();
targetVec=posVec+glm::rotate(targetVec-posVec, among, cacheZ);
isChange=true;
}
void Camera::resetCacheAxis(){
cacheY=glm::normalize(targetVec-posVec);
cacheZ=glm::normalize(upVec);
cacheX=glm::cross(cacheY, cacheZ);
}
and I implement mouse camera control with yaw and pitch
void FPSCamera::rotateScreen(float dx, float dy){
yaw(-dx);
pitch(dy);
}
the problem is still there....
after moving the mouse along a circle path several times.....
The camera should rotate about 2 axis. The up vector and the strafe vector. Rotating about the up vector gives the side to side "look", while rotating about the strafe vector gives the up and down "look".
When you perform the rotation, you need to not only transform the direction, but also the up vector and strafe vector. You don't seem to be doing that here. At least 2 vectors must be changed after a transformation as all 3 vectors must be orthogonal to each other.
Take a look at the following as pseudocode:
public static void MouseLook(float x, float y)
{
Matrix xRot = Matrix.CreateFromAxisAngle(Vector3.Up, x * rotationSpeed);
Matrix yRot = Matrix.CreateFromAxisAngle(Vector3.Right, y * rotationSpeed);
viewMatrix = viewMatrix * yRot * xRot;
// Orthogonalize the matrix
OrthoNormalize();
}
private static void OrthoNormalize()
{
Vector3 zAxis = viewMatrix.Forward;
zAxis.Normalize();
Vector3 xAxis = Vector3.Cross(Vector3.Up,zAxis);
xAxis.Normalize();
Vector3 yAxis = Vector3.Cross(xAxis,zAxis);
yAxis.Normalize();
viewMatrix.Forward = zAxis;
viewMatrix.Up = yAxis;
viewMatrix.Right = xAxis;
}
Check out the following question as it has the answer to what you're looking for: Camera Rotation

Create arrow class

I am trying to create an arrow class that takes two Points. This is what I have:
struct Arrow : Lines { // an Arrow is a Line that has an arrow at the end
Arrow(Point p1, Point p2) // construct an Arrow from two points
:p1(p1), p2(p2) {}
void draw_lines() const;
Point p1;
Point p2;
};
This is what I have of the draw_lines() const function:
void Arrow::draw_lines() const
{
Lines arrow;
arrow.add(p1, p2);
arrow.draw_lines();
}
It works by typing Arrow a(Point(x,y), Point(x1,y1));. It is then supposed to calculate the arrow using (x,y), which becomes p1, and (x1,y1), which becomes p2, as guides to the direction and (x1,y1) as the arrow base point. I would like the arrow to look like this, only solid: --->. How can I calculate the angle that the arrow points? The lines of the arrow head need to be as two x,y coordinates, such as (p2.x, p2.y, (x coordinate of back point of arrow relative to p2), (y coordinate of back point of arrow relative to p2). The .x and .y notation returns the x and y coordinates of Point p.
Thank you for any help.
Here's what it looks like using atan2.
const double pi = 3.1415926535897931;
const int r = 5;
const double join_angle = pi / 6.0; // 30 degrees
const double stem_angle = atan2(p2.y-p1.y, p2.x-p1.x);
Lines arrow;
arrow.add(p1, p2);
arrow.add(p2, Point(p2.x - r*cos(stem_angle+join_angle), p2.y - r*sin(stem_angle+join_angle)));
arrow.add(p2, Point(p2.x - r*cos(stem_angle-join_angle), p2.y - r*sin(stem_angle-join_angle)));
This is exactly the approach you described in your comment:
Ideally, I would like to enter a const int to be added or subtracted to the angle of the slope to create the angle of the arrowhead lines, and another for the length, and it takes care of the nitty gritty point coordinates itself.
Except that double is much better than int for storing the join_angle in radians.
You don't need trigonometric functions like atan. Just normalize the vector from p1 to p2 (you need sqrt for that), get the orthogonal vector to that (easy in 2D) and get the missing two points by adding multiples (factors determine length and width of the arrow head) of these two unit vectors to the end point.