Box2d - Problems calculating impulse with speed and angle - c++

I have a weapon that bounces to the next enemy when it hits.
I first begin by calculating the delta and the getting the angle:
float deltaX = e->m_body->GetPosition().x - m_body->GetPosition().x;
float deltaY = e->m_body->GetPosition().y - m_body->GetPosition().y;
float angle = atan2((deltaY), deltaX) * 180 / M_PI;
Then I convert the angle to a vector and multiply it by 15 (the speed of the projectile):
b2Vec2 vec = b2Vec2(cos(angle*M_PI/180),sin(angle*M_PI/180));
vec *= 15.0f;
Finally, I apply the impulse to the body:
m_body->ApplyLinearImpulse(vec, m_body->GetPosition());
The problem is that the vector must be incorrect as the bullet does not go in the right direction. If I simply output the angle to the next enemy, it tends to output an angle that looks correct so the problem must be in the conversion to a vector.

I don't think you need to use any trigonometry functions here, because you already have the direction:
b2Vec2 direction = e->m_body->GetPosition() - m_body->GetPosition();
direction.Normalize(); // this vector now has length 1
float speed = ...;
m_body->ApplyLinearImpulse( speed * direction, m_body->GetWorldCenter() );

Related

From Euler angles to Quaternions

I am working on a simulation of plane movement. For now, I used Euler angles to transform "body frame" to "world frame" and it works fine.
Recently I learned about quaternions and their advantages over the rotation matrix (gimbal lock) and I tried to implement it using yaw/pitch/roll angles from the simulator.
Quaternion
If I understand correctly the quaternion represents two things. It has an x, y, and z component, which represents the axis about which a rotation will occur. It also has a w component, which represents the amount of rotation which will occur about this axis. In short, a vector, and a float. A quaternion can be represented as 4 element vector:
q=[w,x,y,z]
To calculate result (after full rotation) equation is using:
p'=qpq'
where:
p=[0,x,y,z]-direction vector
q=[w,x,y,z]-rotation
q'=[w,-x,-y,-z]
Algorithm
Create quaternion:
Using wikipedia I create quaternion by rotating around 3 axes (q):
Quaterniond toQuaternion(double yaw, double pitch, double roll) // yaw (Z), pitch (Y), roll (X)
{
//Degree to radius:
yaw = yaw * M_PI / 180;
pitch = pitch * M_PI / 180;
roll = roll * M_PI / 180;
// Abbreviations for the various angular functions
double cy = cos(yaw * 0.5);
double sy = sin(yaw * 0.5);
double cp = cos(pitch * 0.5);
double sp = sin(pitch * 0.5);
double cr = cos(roll * 0.5);
double sr = sin(roll * 0.5);
Quaterniond q;
q.w = cy * cp * cr + sy * sp * sr;
q.x = cy * cp * sr - sy * sp * cr;
q.y = sy * cp * sr + cy * sp * cr;
q.z = sy * cp * cr - cy * sp * sr;
return q;
}
Define plane direction (heading) vector:
p = [0,1,0,0]
Calculate Hamilton product:
p'=qpq'
q'= [w, -qx, -qy, -qz]
p' = (H(H(q, p), q')
Quaterniond HamiltonProduct(Quaterniond u, Quaterniond v)
{
Quaterniond result;
result.w = u.w*v.w - u.x*v.x - u.y*v.y - u.z*v.z;
result.x = u.w*v.x + u.x*v.w + u.y*v.z - u.z*v.y;
result.y = u.w*v.y - u.x*v.z + u.y*v.w + u.z*v.x;
result.z = u.w*v.z + u.x*v.y - u.y*v.x + u.z*v.w;
return result;
}
Result
My result will be a vector:
v=[p'x,p'y,p'z]
It works fine but the same as Euler angle rotation (gimbal lock). Is it because I use also euler angles here? I don't really see how it should work without rotation around 3 axes. Should I rotate around every axis separately?
I will be grateful for any advice and help with understanding this problem.
EDIT (how application works)
1. My application based on data streaming, it means that after every 1ms it checks if there are new data (new orientation of plane).
Example:
At the begging pitch/roll/yaw = 0, after 1ms yaw is changed by 10 degree so application reads pitch=0, roll=0, yaw = 10. After next 1ms yaw is changed again by 20 degrees. So input data will look like this: pitch=0, roll=0, yaw = 30.
2. Create direction quaternion - p
At the begging, I define that direction (head) of my plane is on X axis. So my local direction is
v=[1,0,0]
in quaternion (my p) is
p=[0,1,0,0]
Vector3 LocalDirection, GlobalDirection; //head
Quaterniond p,P,q, Q, pq; //P = p', Q=q'
LocalDirection.x = 1;
LocalDirection.y = 0;
LocalDirection.z = 0;
p.w = 0;
p.x = direction.x;
p.y = direction.y;
p.z = direction.z;
3. Create rotation
After every 1ms I check the rotation angles (Euler) from data streaming and calculate q using toQuaternion
q = toQuaternion(yaw, pitch, roll); // create quaternion after rotation
Q.w = q.w;
Q.x = -q.x;
Q.y = -q.y;
Q.z = -q.z;
4. Calculate "world direction"
Using Hamilton product I calculate quaternion after rotation which is my global direction:
pq = HamiltonProduct(q, p);
P = HamiltonProduct(pq, Q);
GlobalDirection.x = P.x;
GlobalDirection.y = P.y;
GlobalDirection.z = P.z;
5. Repeat 3-4 every 1ms
It seems that your simulation uses Euler angles for rotating objects each frame. You then convert those angles to quaternions afterwards. That won't solve gimbal lock.
Gimbal lock can happen anytime when you add Euler angles to Euler angles. It's not enough to solve this when going from local space to world space. You also need your simulation to use quaternions between frames.
Basically everytime your object is changing its rotation, convert the current rotation to a quaternion, multiply in the new rotation delta, and convert the result back to Euler angles or whatever you use to store rotations.
I'd recommend rewriting your application to use and story only quaternions. Whenever a user does input or some other logic of your game wants to rotate something, you immediately convert that input into a quaternion and feed it into the simulation.
With your toQuaternion and HamiltonProduct you have all the tools you need for that.
EDIT In response to your edit explaining how your application works.
At the begging pitch/roll/yaw = 0, after 1ms yaw is changed by 10 degree so application reads pitch=0, roll=0, yaw = 10. After next 1ms yaw is changed again by 20 degrees. So input data will look like this: pitch=0, roll=0, yaw = 30.
That is where gimbal lock happens. You convert to quaternions after you calculate the rotations. That is wrong. You need to use quaternions in this very first step. Don't do after 1ms yaw is changed by 10 degree so application reads pitch=0, roll=0, yaw = 10, do this:
Store the rotation as quaternion, not as euler angles;
Convert the 10 degree yaw turn into a quaternion;
Multiply the stored quaternion and the 10 degree yaw quaternion;
Store the result.
To clarify: Your steps 2, 3 and 4 are fine. The problem is in step 1.
On a side note, this:
It has an x, y, and z component, which represents the axis about which a rotation will occur. It also has a w component, which represents the amount of rotation which will occur about this axis
is not exactly correct. The components of a quaternion aren't directly axis and angle, they are sin(angle/2) * axis and cos(angle/2) (which is what your toQuaternion method produces). This is important as it gives you nice unit quaternions that form a 4D sphere where every point on the surface (surspace?) represents a rotation in 3D space, beautifully allowing for smooth interpolations between any two rotations.

rotating around objects

I've been trying to make one object orbiting another:
//childX,childY,childZ are my starting coordinates
//here I count distance to the middle of my coordinate plane
float r = (float) Math.sqrt(Math.pow(childX, 2)+Math.pow(childY, 2)+Math.pow(childZ,2));
//here i convert my angles to radians
float alphaToRad = (float) Math.toRadians(findParent(figure.parentId).rotate[1]);//up_down
float betaToRad = (float) Math.toRadians(findParent(figure.parentId).rotate[0]);//left_right
float newX = (float) (r*Math.cos(betaToRad)*Math.cos(alphaToRad));
float newY = (float) (r*Math.cos(betaToRad)*Math.sin(alphaToRad));
float newZ = (float) (r*Math.sin(betaToRad));'
I have coordinates of my starting point(5,5,0) and angles 0° and 0°, so it means, that coordinates shouldn't change after calculating the new ones. But the result is:
newX: 7.071068 newY: 0.0 newZ: 0.0
Every method I try to calculate new coordinates there is always this strange result. What is that 7.07 and how can I get correct result?
#edit
To make my new point relative to the old one I just added angles of old point to the new one:
float alphaToRad = (float) Math.toRadians(findParent(figure.parentId).rotate[1]) + Math.atan(childY/childX);
float betaToRad = (float) Math.toRadians(findParent(figure.parentId).rotate[0]) + Math.asin(childZ/r);
Everything now works like it should have. Solved
7.07 is the value of r in your code, which is the distance of your point from the origin:
sqrt(5 * 5 + 5 * 5) = sqrt(50) = 7.0711
With both angles being zero, all the cos() values will be 1.0, and the sin() values 0.0. Which means that newX becomes r, which is 7.07, and both newY and newZ become 0.0. Which is exactly what you got, so there is no mystery in this result.
What you're basically doing is place the point at a given direction and distance from the origin. The distance is the same as the original distance. The direction is given by the two angles, where both angles being 0.0 corresponds to the x-axis direction.
In other words, what you're missing is that you're not taking the original direction of the point relative to the origin into account. You're placing the point at an absolute direction, based on the two angles, instead of at a direction relative to the original direction of the point.
To rotate the point by the given angles, the easiest approach is to build rotation matrices from the angles, and apply them to your points.

Discontinuity in gluLookAt

This is how I calculate my line of sight vector and the up vector.
ly = sin(inclination);
lx = cos(inclination)*sin(azimuth);
lz = cos(inclination)*cos(azimuth);
uy = sin(inclination + M_PI / 2.0);
ux = cos(inclination + M_PI / 2.0)*sin(azimuth + M_PI);
uz = cos(inclination + M_PI / 2.0)*cos(azimuth + M_PI);
inclination is the angle of the line of sight vector from the xz plane and azimuth is the angle in the xz plane.
This works fine till my inclination reaches 225 degrees. At that point there is a discontinuity in the rotation for some reason. (Note By 225 degrees, I mean its past the upside-down point)
Any ideas as to why this is so?
EDIT: Never mind, figured it out. The azimuth does not need a 180 deg. tilt for the up vector.
I think you are talking of a limit angle of 90 degrees (pi). What you get is a normal behavior. When using gluLookAt, you specify an 'up' vector, used to determine the roll of the camera. In the special case where you are looking upside down, the 'up' vector is parallel to the eye direction vector, so it is not possible to determine the roll of the camera (this problem as an infinite number of solutions, so an arbitrary one is chosen by gluLookAt). May be you should compute this 'up' vector using your inclination and azimuth.

Function to rotate a point around another point

so I've got a math function here that supposed to return a rotated point, and takes an original point, point to rotate around (origin) and radians to rotate it.
However it rotating only at half speed (aka 180 degree movement = 90 degree rotation)
sf::Vector2f RotatePoint(sf::Vector2f origin, sf::Vector2f point, float radian) {
float s = sin(radian);
float c = cos(radian);
// translate point back to origin:
point.x -= origin.x;
point.y -= origin.y;
// rotate point
float xnew = point.x * c - point.y * s;
float ynew = point.x * s + point.y * c;
// translate point back to global coords:
sf::Vector2f TranslatedPoint;
TranslatedPoint.x = xnew + origin.x;
TranslatedPoint.y = ynew + origin.y;
return TranslatedPoint;
}
The function looks ok to me. The rotation, in case you're wondering, is just multiplying a vector by the 2D Euclidean rotation matrix (http://en.wikipedia.org/wiki/Rotation_matrix ). The only errors I can think of are some misunderstanding of the usage of the function. E.g. that 2*PI radians = 360 degrees, or that the rotation is counterclockwise.
Your code seems fine. Do we agree that 180° in degrees is Pi in radians ?
The function is fine. However, you might want to fresh up on your linear algebra a bit: You basically compute
return rotation * (point - origin) + origin;
with rotation as the matrix consisting of cos(radian) on the diagonals and +/- sin(radian) on the off-diagonals. So, the whole function is a one-liner if you let your linear algebra library compute that matrix; and if you factor out the -origin part (remember, linear algebra is linear), it becomes:
return rotation * point + ( - rotation * origin + origin );
where the second part is point-invariant and can be precomputed.

Convert Mouse pos into direction and back

I want to ask what would be the best formula to convert mouse X,Y position into one of 16 directiones from player position.
I work in c++ ,sfml 1.6 so I get every position easily, but I dont know how to convert them based on angle from player position or something. (I was never good on math and for more than 4 directions if statements looks too complex).
Also I want to send it to server which converts direction back into delta X,Y so he can do something like:
player.Move(deltaX * speed * GetElapsedTime(), ...Y);
The "easiest" way would be to convert your two sets of co-ordinates (one for current player position, one for current mouse position) into an angle relative to the player's position, where an angle of 0 is the line straight ahead of the player (or north, depending on how your game works). Then each of your sixteen directions would translate to a given 22.5 degree interval.
However, since you said you're bad at math, I imagine you're looking for something more concrete than that.
You could use atan2 to get the angle between the mouse position and the positive X axis:
#include <cmath>
float player_x = ...;
float player_y = ...;
float mouse_x = ...;
float mouse_y = ...;
float angle = std::atan2(mouse_y - player_y, mouse_x - player_x);
The angle returned by std::atan2() is a value between -M_PI (exclusive) and M_PI (inclusive):
-M_PI Left (-180°)
-0.5 * M_PI Down (-90°)
0 Right (0°)
0.5 * M_PI Up (90°)
M_PI Left (180°)
You can transform this value depending on how you want your mapping to "one of 16 directions", i.e., depending on what value you want to assign to which discrete direction.
Given the angle, getting a unit vector to represent the X/Y delta is quite easy, too:
float dx = std::cos(angle);
float dy = std::sin(angle);