Wave vector in 2 dimensions - c++

So I'm trying to make the player shoot a bullet that goes towards the mouse in a wavey pattern. I can get the bullet to move in a wavey pattern (albeit not really how I predicted), but not towards the mouse.
Vector2 BulletFun::sine(Vector2 vec) {
float w = (2 * PI) / 1000; // Where 1000 is the period
float waveNum = (2 * PI) / 5; // Where 5 is the wavelength
Vector2 k(0.0F, waveNum);
float t = k.dot(vec) - (w * _time);
float x = 5 * cos(t); // Where 5 is the amplitude
float y = 5 * sin(t);
Vector2 result(x, y);
return result;
}
Right now the speed isn't much of a concern, that shouldn't be too much of a problem once I have this figured out. I do get some angle change, but it seems to be reversed and only 1/8th a circle.
I'm probably miscalculating something somewhere. I just kind of learned about wave vectors.
I've tried a few other things, such as 1 dimensional travelling waves and another thing involving adjusting a normal sine wave by vec. Which had more or less the same result.
Thanks!
EDIT:
vec is the displacement from the player's location to the mouse click location. The return is a new vector that is adjusted to follow a wave pattern, BulletFun::sine is called each time the bullet receives and update.
The setup is something like this:
void Bullet::update() {
_velocity = BulletFun::sine(_displacement);
_location.add(_velocity); // add is a property of Tuple
// which Vector2 and Point2 inherit
}

In pseudocode, what you need to do is the following:
waveVector = Vector2(travelDistance,amplitude*cos(2*PI*frequency*travelDistance/unitDistance);
cosTheta = directionVector.norm().dot(waveVector.norm());
theta = acos(cosTheta);
waveVector.rotate(theta);
waveVector.translate(originPosition);
That should compute the wave vector in a traditional coordinate frame, and then rotate it to the local coordinate frame of the direction vector (where the direction vector is the local x-axis), and then translate the wave vector relative to your desired origin position of the wave beam or whatever...
This will result in a function very similar to
Vector2
BulletFun::sine(Bullet _bullet, float _amplitude, float _frequency, float _unitDistance)
{
float displacement = _bullet.getDisplacement();
float omega = 2.0f * PI * _frequency * _displacement / _unitDistance;
// Compute the wave coordinate on the traditional, untransformed
// Cartesian coordinate frame.
Vector2 wave(_displacement, _amplitude * cos(omega));
// The dot product of two unit vectors is the cosine of the
// angle between them.
float cosTheta = _bullet.getDirection().normalize().dot(wave.normalize());
float theta = acos(cosTheta);
// Translate and rotate the wave coordinate onto
// the direction vector.
wave.translate(_bullet.origin());
wave.rotate(theta);
}

Related

Rotate vector to new base vector

In raycaster I am developing I am trying to implement hemisphere random sampling, with option to rotate hemisphere to direction and then take random point.
First version worked fine because sampling was uniform, and change of direction was just swapping to other hemisphere, which was simple.
Vec3f UniformSampleSphere() {
const Vec2f& u = GetVec2f(); // get two random numbers
float z = 1 - 2 * u.x;
float r = std::sqrt(std::max((float)0, (float)1 - z * z));
float phi = 2 * PI_F * u.y;
return Vec3f(r * std::cos(phi), r * std::sin(phi), z);
}
Vec3f GetRandomOnHemiSphere(Vec3f direction) {
auto toReturn = GetRandomOnSphere();
if (Dot(toReturn - direction, toReturn) < 0)
toReturn = -toReturn;
return toReturn;
}
But with cosine weighted hemisphere sampling I am in trouble to rotate properly and find random direction in correctly rotated hemisphere.
On picture's left we can see what is working now, and on right is after applying magic rotation that is that big deal I want.
So final function will be something like this:
Vec3f GetRandomOnHemiSphere(Vec3f direction) {
auto toReturn = CosineSampleHemisphere();
/*
Some magic here that rotates to correct direction of hemisphere
*/
return toReturn;
}
I used code from Socine weighted hemisphere sampling.

2d Elastic Collision with Circles

I've seen there is a lot of posts about this already but I can't find one that relates to what I want to do,
I used the formula from here:
https://www.vobarian.com/collisions/2dcollisions2.pdf
As well as this one:
https://www.plasmaphysics.org.uk/programs/coll2d_cpp.htm
I think they area basically the same thing, now my problem is one of my circles is always static, and what I want is when the other circle hits it straight on, I want it to bounce back with the same speed, but these formulas have the circle stop still, presumably as it would pass it's energy to the other circle which would then move away.
I tried doing things like bounce = vel.x pre collision - vel.y post collision and add or subtract that to vel.x post collision and it kinda works but not really, the angles are wrong and depending on which direction the ball is coming from it may bounce up instead of down, left instead of right,
would probably require a lot of if/else statements to get to work at all.
Can someone suggest something?
here's the code for the function :
void Collision2(sf::CircleShape* b1, sf::CircleShape* b2, sf::Vector2f vel1,sf::Vector2f& vel2) {
//vel1 is 0,0 but i might want to use it later
//mass
float m1 = 10;
float m2 = 10;
//normal vector
sf::Vector2f nVec((b2->getPosition().x - b1->getPosition().x), (b2->getPosition().y - b1->getPosition().y));
//unit vector
sf::Vector2f uNVec(nVec / sqrt((nVec.x * nVec.x) + (nVec.y * nVec.y)));
//unit tangent vec
sf::Vector2f uTVec(-uNVec.y, uNVec.x);
float v1n = (uNVec.x * vel1.x) + (uNVec.y * vel1.y);
float v2n = (uNVec.x * vel2.x) + (uNVec.y * vel2.y);
float v1t = uTVec.x * vel1.x + uTVec.y * vel2.y;
float v2t = (uTVec.x * vel2.x) + (uTVec.y * vel2.y);
//v1t and v1n after collision
float v1tN = v1t;
float v2tN = v2t;
float v1nN = (v1n * (m1 - m2) + (2 * m2) * v2n) / (m1 + m2);
float v2nN = (v2n * (m2 - m1) + (2 * m1) * v1n) / (m1 + m2);
//new velocities
sf::Vector2f vel1N(v1nN*uNVec);
sf::Vector2f vel1tN(v1tN * uTVec);
sf::Vector2f vel2N(v2nN * uNVec);
sf::Vector2f vel2tN(v2tN * uTVec);
vel1 = (vel1N + vel1tN);
vel2 = (vel2N + vel2tN);
}
Physics part
The sources you added illustrate the physics behind it very well. when the two balls collide they transfer momentum between them. In an elastic collision this transfer keeps the energy of the system, the same.
We can think of the collision in terms of inertia and momentum, rather than starting from velocity. The kinetic energy of a body is generally p^2/(2m), so if we transfer dp from the moving body then we will have change in energy: dE = -pdp/m + dp^2/(2m) + dp^2/(2M) = 0. Here m is the moving and M is the stationary mass. Rearranging gives pdp/m = dp^2*(1/(2m) + 1/(2M)). We can consider m = M yielding p = dp (i.e. All moment is transferred (Note: this is a simplistic view, only dealing with head on collisions)). In the limit where the stationary object is massive however (M >> m) the result will be dp = 2p, simply bouncing off.
Programming
You can achieve the results by setting M to the maximum allowed float value (if I recall 1/inf == NaN in the IEEE standard so that doesn't work unfortunately). Alternatively you can do the collision within the circle by creating custom classes like:
class Circle : public sf::CircleShape{
public:
virtual collide (Circle*);
}
class StaticCircle : public Circle{
public:
collide (Circle*) override;
}
in the second one you can omit any terms where you divide by the mass of the circle, as it is in essence infinite.

Moving an object in the direction of the camera

I'm making a project where I need to move a player in any direction using an analog stick. I'm limited to specific functions and I only have the positions of the camera and the player and the analog stick. The camera is always pointed to the player.
vec2 &leftStick = getLeftStick(-1); // results in an x and a y, both ranging from -1 to 1.
vec3 *playerPos = getTrans(player);
vec3 *cameraPos = getCameraPos(player, 0);
playerPos->x += leftStick.x * 10.0f;
playerPos->z -= leftStick.y * 10.0f;
This code works to move the player, however its using the orientation of the world. I need it where holding up on the analog stick (left stick y = 1) makes the player go forward, no matter what way the player/camera are facing.
My solution, thank you #Borgleader for a majority of it.
I found an equation to find the distance and velocity for the x and z online, then I tested a bunch of combinations until it worked properly. Not a good way to do this but it worked out.
// this all replaces the last two lines of the previous code snippet
float speed = 30.0f;
float d = sqrt(powf(playerPos->x - cameraPos->x, 2) + powf(playerPos->z - cameraPos->z, 2));
float vx = (speed/d)*(playerPos->x - cameraPos->x);
float vz = (speed/d)*(playerPos->z - cameraPos->z);
playerPos->x -= leftStick.x * vz;
playerPos->z += leftStick.x * vx;
playerPos->x += leftStick.y * vx;
playerPos->z += leftStick.y * vz;

Getting a Virtual Trackball to work from any viewing angle

I am currently trying to work on getting my virtual trackball to work from any angle. When I am looking at it from the z axis, it seems to work fine. I hold my mouse down, and move the mouse up... the rotation will move accordingly.
Now, if I change my viewing angle / position of my camera and try to move my mouse. The rotation will occur as if I were looking from the z axis. I cannot come up with a good way to get this to work.
Here is the code:
void Renderer::mouseMoveEvent(QMouseEvent *e)
{
// Get coordinates
int x = e->x();
int y = e->y();
if (isLeftButtonPressed)
{
// project current screen coordinates onto hemi sphere
Point sphere = projScreenCoord(x,y);
// find axis by taking cross product of current and previous hemi points
axis = Point::cross(previousPoint, sphere);
// angle can be found from magnitude of cross product
double length = sqrt( axis.x * axis.x + axis.y * axis.y + axis.z * axis.z );
// Normalize
axis = axis / length;
double lengthPrev = sqrt( previousPoint.x * previousPoint.x + previousPoint.y * previousPoint.y + previousPoint.z * previousPoint.z );
double lengthCur = sqrt( sphere.x * sphere.x + sphere.y * sphere.y + sphere.z * sphere.z );
angle = asin(length / (lengthPrev * lengthCur));
// Convert into Degrees
angle = angle * 180 / M_PI;
// 'add' this rotation matrix to our 'total' rotation matrix
glPushMatrix(); // save the old matrix so we don't mess anything up
glLoadIdentity();
glRotatef(angle, axis[0], axis[1], axis[2]); // our newly calculated rotation
glMultMatrixf(rotmatrix); // our previous rotation matrix
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*) rotmatrix); // we've let OpenGL do our matrix mult for us, now get this result & store it
glPopMatrix(); // return modelview to its old value;
}
// Project screen coordinates onto a unit hemisphere
Point Renderer::projScreenCoord(int x, int y)
{
// find projected x & y coordinates
double xSphere = ((double)x/width)*2.0 - 1.0;
double ySphere = ( 1 - ((double)y/height)) * 2.0 - 1.0;
double temp = 1.0 - xSphere*xSphere - ySphere*ySphere;
// Do a check so you dont do a sqrt of a negative number
double zSphere;
if (temp < 0){ zSphere = 0.0;}
else
{zSphere = sqrt(temp);}
Point sphere(xSphere, ySphere, zSphere);
// return the point on the sphere
return sphere;
}
I am still fairly new at this. Sorry for the trouble and thanks for all the help =)
The usual way involves quaternions. E.g., in sample code originally from SGI.

Rotating coordinates around an axis

I'm representing a shape as a set of coordinates in 3D, I'm trying to rotate the whole object around an axis (In this case the Z axis, but I'd like to rotate around all three once I get it working).
I've written some code to do this using a rotation matrix:
//Coord is a 3D vector of floats
//pos is a coordinate
//angles is a 3d vector, each component is the angle of rotation around the component axis
//in radians
Coord<float> Polymers::rotateByMatrix(Coord<float> pos, const Coord<float> &angles)
{
float xrot = angles[0];
float yrot = angles[1];
float zrot = angles[2];
//z axis rotation
pos[0] = (cosf(zrot) * pos[0] - (sinf(zrot) * pos[1]));
pos[1] = (sinf(zrot) * pos[0] + cosf(zrot) * pos[1]);
return pos;
}
The image below shows the object I'm trying to rotate (looking down the Z axis) before the rotation is attempted, each small sphere indicates one of the coordinates I'm trying to rotate
alt text http://www.cs.nott.ac.uk/~jqs/notsquashed.png
The rotation is performed for the object by the following code:
//loop over each coordinate in the object
for (int k=start; k<finish; ++k)
{
Coord<float> pos = mp[k-start];
//move object away from origin to test rotation around origin
pos += Coord<float>(5.0,5.0,5.0);
pos = rotateByMatrix(pos, rots);
//wrap particle position
//these bits of code just wrap the coordinates around if the are
//outside of the volume, and write the results to the positions
//array and so shouldn't affect the rotation.
for (int l=0; l<3; ++l)
{
//wrap to ensure torroidal space
if (pos[l] < origin[l]) pos[l] += dims[l];
if (pos[l] >= (origin[l] + dims[l])) pos[l] -= dims[l];
parts->m_hPos[k * 4 + l] = pos[l];
}
}
The problem is that when I perform the rotation in this way, with the angles parameter set to (0.0,0.0,1.0) it works (sort of), but the object gets deformed, like so:
alt text http://www.cs.nott.ac.uk/~jqs/squashed.png
which is not what I want. Can anyone tell me what I'm doing wrong and how I can rotate the entire object around the axis without deforming it?
Thanks
nodlams
Where you do your rotation in rotateByMatrix, you compute the new pos[0], but then feed that into the next line for computing the new pos[1]. So the pos[0] you're using to compute the new pos[1] is not the input, but the output. Store the result in a temp var and return that.
Coord<float> tmp;
tmp[0] = (cosf(zrot) * pos[0] - (sinf(zrot) * pos[1]));
tmp[1] = (sinf(zrot) * pos[0] + cosf(zrot) * pos[1]);
return tmp;
Also, pass the pos into the function as a const reference.
const Coord<float> &pos
Plus you should compute the sin and cos values once, store them in temporaries and reuse them.