How to linearly interpolate to destination that is not constant - c++

If I want to linearly interpolate from point A (source) to point B (destination) on a 2d plane I could do it like this. Vector2 here is a struct consisting of x and y floats.
Vector2 Interpolate(Vector2 source, Vector2 destination, float delta) {
return source+(destination-source)*delta;
}
void OnEachUpdate() {
delta += 0.05f;
delta = max(delta, 1.0f);
currentVector = Interpolate(source, destination, delta);
}
This way I can interpolate from source to destination with a given delta. However what if I have destination vector that is not constant? Say the user can change the destination by pointing a mouse cursor on a 2d plane. An object should linearly interpolate to the destination vector.
I could do something like this but it's not a linear interpolation, but more like sigmoidal, which is not what I want.
delta = 0.05f;
currentVector += Interpolate(currentVector, destination, delta);

Since you said you want to keep the speed constant, I assume you want this:
float speed = whatever;
float delta_x = target_x - current_x;
float delta_y = target_y - current_y;
float dist_sqr = delta_x*delta_x + delta_y*delta_y;
if (dist_sqr <= speed*speed)
{
// The destination is reached.
current_x = target_x;
current_y = target_y;
}
else
{
float dist = std::sqrt(dist_sqr);
current_x += delta_x / dist * speed;
current_y += delta_y / dist * speed;
}

Related

Using The Dot Product to determine whether an object is on the left hand side or right hand side of the direction of the object

so I currently am trying to create some method which when taking in a simulation vehicles position, direction, and an objects position, Will determine whether or not the object lies on the right and side or left hand side of that vehicles direction. This is what i have implemented so far (Note I am in a 2D co-ord system):
This is the code block that uses the method
void Class::leftOrRight()
{
// Clearing both _lhsCones and _rhsCones vectors
_rhsCones.clear();
_lhsCones.clear();
for (int i =0; i < _cones.size(); i++)
{
if (dotAngleFromYaw(_x, _y, _cones[i].x(), _cones[i].y(), _yaw) > 0)
{
_lhsCones.push_back(_cones[i]);
}
else
{
_rhsCones.push_back(_cones[i]);
}
}
return;
}
This is the code block which computes the angle
double Class::dotAngleFromYaw(double xCar, double yCar, double xCone, double yCone, double yawCar)
{
double iOne = cos(yawCar);
double jOne = sin(yawCar);
double iTwo = xCone - xCar;
double jTwo = yCone - yCar;
//ensure to normalise the vector two
double magTwo = std::sqrt(std::pow(iTwo, 2) + std::pow(jTwo, 2));
iTwo = iTwo / magTwo;
jTwo = jTwo / magTwo;
double theta = acos((iOne * iTwo) + (jOne * jTwo)); // in radians
return theta;
}
My issue with this is that dotAngleFromYaw(0,0,0,1,0) = +pi/2 and dotAngleFromYaw(0,0,0,-1,0) = +pi/2 hence the if statements fail to sort the cones.
Any help would be great
*Adjustments made from comment suggestions
I have change the sort method as follows
double Class::indicateSide(double xCar, double yCar, double xCone, double yCone, double yawCar)
{
// Compute the i and j compoents of the yaw measurment as a unit vector i.e Vector Mag = 1
double iOne = cos(yawCar);
double jOne = sin(yawCar);
// Create the Car to Cone Vector
double iTwo = xCone - xCar;
double jTwo = yCone - yCar;
//ensure to normalise the vCar to Cone Vector
double magTwo = std::sqrt(std::pow(iTwo, 2) + std::pow(jTwo, 2));
iTwo = iTwo / magTwo;
jTwo = jTwo / magTwo;
// // Using the transformation Matrix with Theta = yaw (angle in radians) transform the axis to the augmented 2D space
// double Ex = cos(yawCar)*iOne - sin(yawCar)*jOne;
// double Ey = sin(yawCar)*iOne + cos(yawCar)*jOne;
// Take the Cross Product of < Ex, 0 > x < x', y' > where x', y' have the same location in the simulation space.
double result = iOne*jTwo - jOne*iTwo;
return result;
}
However I still am having issues defining the left and right, note that I have also become aware that objects behind the vehicle are still passed to every instance of the array of objects to be evaluated and hence I have implemented a dot product check elsewhere that seems to work fine for now, which is why I have not included it here I can make another adjustment to the post to include said code. I did try to implement the co-ordinate system transformation however i did not see improvements compared to when the added lines are not commented out and implemented.
Any further feedback is greatly appreciated
If the angle does not matter and you only want to know whether "left or right" I'd go for another approach.
Set up a plane that has xCar and yCar on its surface. When setting it up it's up to you how to define the plane's normal i.e. the side its facing to.
After that you can apply the dot-product to determine the 'sign' indicating which side it's on.
Note that dot product does not provide information about left/right position.
Sign of dot product says whether position is ahead or backward.
To get left/right side, you need to check sign of cross product
cross = iOne * jTwo - jOne * iTwo
(note subtraction and i/j alternation)
To see the difference between dot and cross product info:
Quick test. Mathematical coordinate system (CCW) is used (left/right depends on CW/CCW)
BTW, in kinematics simulations it is worth to store components of direction vector rather than angle.
#define _USE_MATH_DEFINES // для C++
#include <cmath>
#include <iostream>
void check_target(float carx, float cary, float dirx, float diry, float tx, float ty) {
float cross = (tx - carx) * diry - (ty - cary) * dirx;
float dot = (tx - carx) * dirx + (ty - cary) * diry;
if (cross >= 0) {
if (dot >= 0)
std::cout << "ahead right\n";
else
std::cout << "behind right\n";
}
else {
if (dot >= 0)
std::cout << "ahead left\n";
else
std::cout << "behind left\n";
}
}
int main()
{
float carx, cary, car_dir_angle, dirx, diry;
float tx, ty;
carx = 1;
cary = 1;
car_dir_angle = M_PI / 4;
dirx = cos(car_dir_angle);
diry = sin(car_dir_angle);
check_target(carx, cary, dirx, diry, 2, 3);
check_target(carx, cary, dirx, diry, 2, 1);
check_target(carx, cary, dirx, diry, 1, 0);
check_target(carx, cary, dirx, diry, 0, 1);
}

How to get the angle (pitch/yaw) between two 3D vectors for an autoaim

I'm trying to get the angles between two vectors (My Camera Position and Enemy Position) to create an autoaim/aimbot.
The game is Unity based, it uses the left handed coordinate system. X Y Z is right, up, forward.
The game also uses degrees.
Here is the pseudocode I am trying but its failing to give me the proper pitch/yaw.
diff = camera_position - enemy_position
hypotenuse = sqrt(diff.x*diff.x + diff.y*diff.y)
angle.x = asinf(diff.z / hypotenuse) * (180 / PI);
angle.y = atan2(diff.y / diff.x) * (180 / PI);
angle.z = 0.0f;
Can someone help me with this? I am terrible at math.
I'm trying to get the angles between two vectors (My Camera Position
and Enemy Position)
In Unity:
Use the Angle function from Vector3 structure.
float angle = Vector3.Angle(camera_position, enemy_position);
Or Individual angles:
float angleX = Vector3.Angle(new Vector3(camera_position.x, 0, 0), new Vector3(enemy_position.x, 0, 0));
float angleY = Vector3.Angle(new Vector3(0, camera_position.y, 0), new Vector3(0, enemy_position.y, 0));
float angleZ = Vector3.Angle(new Vector3(0, 0, camera_position.z), new Vector3(0, 0, enemy_position.z));
EDIT:
I'm not using the Unity engine. This is a separate module I am
creating to rig my own autoaim. I'm trying to do get the proper math
itself.
In C++:
The code is explained in the Angle function below which is the last function
#include <iostream>
#include <numeric> //for inner_product
#include <vector> //For vector
#include <math.h> //For sqrt, acos and M_PI
float Dot(std::vector<float> lhs, std::vector<float> rhs);
float magnitude(std::vector<float> vec3);
float Angle(std::vector<float> from, std::vector<float> to);
std::vector<float> normalise();
int main()
{
std::vector<float> from{3, 1, -2};
std::vector<float> to{5, -3, -7 };
float angle = Angle(from,to);
std::cout<<"Angle: "<<angle<<std::endl;
return 0;
}
//Find Dot/ Scalar product
float Dot(std::vector<float> lhs, std::vector<float> rhs){
return std::inner_product(lhs.begin(), lhs.end(), rhs.begin(), 0);
}
//Find the magnitude of the Vector
float magnitude(std::vector<float> vec3)//<! Vector magnitude
{
return sqrt((vec3[0] * vec3[0]) + (vec3[1] * vec3[1]) + (vec3[2] * vec3[2]));
}
//Normalize Vector. Not needed here
std::vector<float> normalise(std::vector<float> vect)
{
std::vector<float> temp{0, 0, 0};
float length = magnitude(vect);
temp[0] = vect[0]/length;
temp[1] = vect[1]/length;
temp[2] = vect[2]/length;
return temp;
}
float Angle(std::vector<float> from, std::vector<float> to){
//Find the scalar/dot product of the provided 2 Vectors
float dotProduct = Dot(from, to);
//Find the product of both magnitudes of the vectors then divide dot from it
dotProduct = dotProduct / (magnitude(from) * magnitude(to));
//Get the arc cosin of the angle, you now have your angle in radians
float arcAcos = acos(dotProduct);
//Convert to degrees by Multiplying the arc cosin by 180/M_PI
float angle = arcAcos * 180 / M_PI;
return angle;
}
To calculate the angle between two 3d coordinates, in degrees you can use this CalcAngle Function:
#include <algorithm>
#define PI 3.1415927f
struct vec3
{
float x, y, z;
}
vec3 Subtract(vec3 src, vec3 dst)
{
vec3 diff;
diff.x = src.x - dst.x;
diff.y = src.y - dst.y;
diff.z = src.z - dst.z;
return diff;
}
float Magnitude(vec3 vec)
{
return sqrtf(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);
}
float Distance(vec3 src, vec3 dst)
{
vec3 diff = Subtract(src, dst);
return Magnitude(diff);
}
vec3 CalcAngle(vec3 src, vec3 dst)
{
vec3 angle;
angle.x = -atan2f(dst.x - src.x, dst.y - src.y) / PI * 180.0f + 180.0f;
angle.y = asinf((dst.z - src.z) / Distance(src, dst)) * 180.0f / PI;
angle.z = 0.0f;
return angle;
}
Complications:
Not all games use the same technique for angles and positions. Min and Max values for x, y and z angles can be different in every game. The basic idea is the same in all games, they just require minor modification to match each game. For example, in the game the code was written for, the X value has to be made negative at the end for it to work.
Another complication is X, Y and Z don't always represent the same variables in both coordinates and angle vec3s.

How to scale the rotation of a quaternion

I am trying to do the equivalent of multiplying the velocity by the time between frames. I would imagine that doing this for quaternions would be done by raising them to a power. I have code to rotate an object based on my mouse movements. It has a main loop running at one frame rate and a physics loop running at a fixed frame rate. Here is the relevant part of the main loop:
glfwPollEvents();
Input::update();
window.clear(0,0,0,1);
rigidBody.angularVelocity *= glm::angleAxis(0.001f * Input::deltaMouse().x, glm::vec3(0,1,0));
rigidBody.angularVelocity *= glm::angleAxis(0.001f * Input::deltaMouse().y, glm::vec3(1,0,0));
if(Input::getKey(Input::KEY_A))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(1,0,0);
}
if(Input::getKey(Input::KEY_D))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(1,0,0);
}
if(Input::getKey(Input::KEY_W))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(0,0,1);
}
if(Input::getKey(Input::KEY_S))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(0,0,1);
}
if(Input::getKey(Input::KEY_LCONTROL))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(0,1,0);
}
if(Input::getKey(Input::KEY_LSHIFT))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(0,1,0);
}
Here is the relevant part of the physics loop:
for(int i = 0; i < *numRigidBodies; i++)
{
rigidBodies[i].transform->getPos() += rigidBodies[i].velocity;
rigidBodies[i].transform->getRot() *= rigidBodies[i].angularVelocity;
}
rigidBodies[0].angularVelocity = glm::quat();
rigidBodies[0].velocity = glm::vec3();
This works fine, but when I try raising angular velocity to a power with glm::pow, the object rotates randomly and does not follow my mouse. I realize I could do this with a line of code like
rigidBodies[i].transform->getRot() *= glm::angleAxis((float)Time::getFixedDelta() * glm::angle(rigidBodies[i].angularVelocity), glm::axis(rigidBodies[i].angularVelocity));
but this seems needlessly complicated for the task. What is causing this issue, and how can I fix it?
Not sure exactly how to do it with the API you're using, but basically, you would use Quaternion::Slerp(). Slerp means "spherical linear interpolation".
Something like this(pseudocode) should work:
auto& rot = rigidBodies[i].transform->getRot();
auto goal = rigidBodies[i].angularVelocity * rot;
rot = rot.slerp(rot, goal, Time::deltaTime);
Edit:
I should note that this is not how I would approach this problem. I would just store the rotation around the X and Y axis as scalars and construct a new quaternion from them each frame.
Please excuse the sloppy pseudo code:
// previous x and y positions, could probably be set in MouseDown event
float lastX = ...;
float lastY = ...;
float xRotation = 0;
float yRotation = 0;
float rotationSpeed = 1.0;
void OnMouseMove(float x, float y) {
float dx = x - lastX;
float dy = y - lastY;
lastX = x;
lastY = y;
xRotation += dy * rotationSpeed * Time::deltaTime;
yRotation += dx * rotationSpeed * Time::deltaTime;
rigidBodies[i].transform->getRot() = eulerQuat(xRotation, yRotation, 0);
}
Turns out angular velocity is usually represented as a 3d vector where the direction is the axis and the magnitude is the angular speed. Replace this line of code:
rigidBodies[i].transform->getRot() *= rigidBodies[i].angularVelocity;
with this:
if(rigidBodies[i].angularVelocity != glm::vec3())
rigidBodies[i].transform->getRot() *= glm::quat(rigidBodies[i].angularVelocity * float(Time::getFixedDelta()));
and the physics system works as expected. The if check makes sure that angular speed is not 0.

Rotating around a sphere using OpenGL and gluLookAt

Alright, so I'm trying to click and drag to rotate around an object using C++ and OpenGL. The way I have it is to use gluLookAt centered at the origin and I'm getting coordinates for the eye by using parametric equations for a sphere (eyex = 2* cos(theta) * sin(phi); eyey = 2* sin(theta) * sin(phi); eyez = 2* cos(phi);). This works mostly, as I can click and rotate horizontally, but when I try to rotate vertically it makes tight circles instead of rotating vertically. I'm trying to get the up vector by using the position of the camera and a vecter at a 90 degree angle along the x-z plane and taking the cross product of that.
The code I have is as follows:
double dotProduct(double v1[], double v2[]) {
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}
void mouseDown(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
xpos = x;
ypos = y;
}
}
void mouseMovement(int x, int y) {
diffx = x - xpos;
diffy = y - ypos;
xpos = x;
ypos = y;
}
void camera (void) {
theta += 2*PI * (-diffy/glutGet(GLUT_SCREEN_HEIGHT));
phi += PI * (-diffx/glutGet(GLUT_WINDOW_WIDTH));
eyex = 2* cos(theta) * sin(phi);
eyey = 2* sin(theta) * sin(phi);
eyez = 2* cos(phi);
double rightv[3], rightt[3], eyes[3];
rightv[0] = 2* cos(theta + 2/PI) * sin(phi);
rightv[1] = 0;
rightv[2] = 2* cos(phi);
rightt[0] = rightv[0];
rightt[1] = rightv[1];
rightt[2] = rightv[2];
rightv[0] = rightv[0] / sqrt(dotProduct(rightt, rightt));
rightv[1] = rightv[1] / sqrt(dotProduct(rightt, rightt));
rightv[2] = rightv[2] / sqrt(dotProduct(rightt, rightt));
eyes[0] = eyex;
eyes[1] = eyey;
eyes[2] = eyez;
upx = (eyey/sqrt(dotProduct(eyes,eyes)))*rightv[2] + (eyez/sqrt(dotProduct(eyes,eyes)))*rightv[1];
upy = (eyez/sqrt(dotProduct(eyes,eyes)))*rightv[0] + (eyex/sqrt(dotProduct(eyes,eyes)))*rightv[2];
upz = (eyex/sqrt(dotProduct(eyes,eyes)))*rightv[1] + (eyey/sqrt(dotProduct(eyes,eyes)))*rightv[0];
diffx = 0;
diffy = 0;
}
I am somewhat basing things off of this but it doesn't work, so I tried my way instead.
This isn't exactly a solution for the way you are doing it but I did something similar the other day. I did it by using DX's D3DXMatrixRotationAxis and D3DXVec3TransformCoord The math behind the D3DXMatrixRotationAxis method can be found at the bottom of the following page: D3DXMatrixRotationAxis Math use this if you are unable to use DX. This will allow you to rotate around any axis you pass in. In my object code I keep track of a direction and up vector and I simply rotate each of these around the axis of movement(in your case the yaw and pitch).
To implement the fixed distance camera like this I would simply do the dot product of the current camera location and the origin location (if this never changes then you can simply do it once.) and then move the camera to the origin rotate it the amount you need then move it back with its new direction and up values.

Am I converting local space to world space coordinates properly?

I'm trying to create a bone and IK system. Below is the method that is recursive and that calculates the absolute positions and absolute angles of each bone. I call it with the root bone and zero'd parameters. It works fine, but when I try to use CCD IK I get discrepancies between the resulting end point and the calculated one. Therefore maybe I'm doing this wrong even though it works.
Thanks
void Skeleton::_updateBones( Bone* root,float realStartX, float realStartY, float realStartAngle )
{
if(!root->isRelative())
{
realStartX = 0.0f;
realStartY = 0.0f;
realStartAngle = 0.0f;
}
realStartX += root->getX();
realStartY += root->getY();
realStartAngle += root->getAngle();
float vecX = sin(realStartAngle);
float vecY = cos(realStartAngle);
realStartX += (vecX * root->getLength());
realStartY += (vecY * root->getLength());
root->setFrame(realStartX,realStartY,realStartAngle);
float angle = fmod(realStartAngle,2.0f * 3.141592f);
if( angle < -3.141592f )
angle += (2.0f * 3.141592);
else if( angle > 3.141592f )
angle -= (2.0f * 3.141592f);
for(std::list<Bone>::iterator it = root->begin(); it != root->end(); ++it)
{
_updateBones(&(*it),realStartX,realStartY,angle);
}
}
This looks wrong.
float vecX = sin(realStartAngle);
float vecY = cos(realStartAngle);
Swap sin() and cos().
float vecX = cos(realStartAngle);
float vecY = sin(realStartAngle);