Here is what I'm trying to do. I'm trying to make a bullet out of the center of the screen. I have an x and y rotation angle. The problem is the Y (which is modified by rotation on the x) is really not working as intended. Here is what I have.
float yrotrad, xrotrad;
yrotrad = (Camera.roty / 180.0f * 3.141592654f);
xrotrad = (Camera.rotx / 180.0f * 3.141592654f);
Vertex3f Pos;
// get camera position
pls.x = Camera.x;
pls.y = Camera.y;
pls.z = Camera.z;
for(float i = 0; i < 60; i++)
{
//add the rotation vector
pls.x += float(sin(yrotrad)) ;
pls.z -= float(cos(yrotrad)) ;
pls.y += float(sin(twopi - xrotrad));
//translate camera coords to cube coords
Pos.x = ceil(pls.x / 3);
Pos.y = ceil((pls.y) / 3);
Pos.z = ceil(pls.z / 3);
if(!CubeIsEmpty(Pos.x,Pos.y,Pos.z)) //remove first cube that made contact
{
delete GetCube(Pos.x,Pos.y,Pos.z);
SetCube(0,Pos.x,Pos.y,Pos.z);
return;
}
}
This is almost identical to how I move the player, I add the directional vector to the camera then find which cube the player is on. If I remove the pls.y += float(sin(twopi - xrotrad)); then I clearly see that on the X and Z, everything is pointing as it should. When I add pls.y += float(sin(twopi - xrotrad)); then it almost works, but not quite, what I observed from rendering out spheres of the trajector is that the furthur up or down I look, the more offset it becomes rather than stay alligned to the camera's center. What am I doing wrong?
Thanks
What basically happens is very difficult to explain, I'd expect the bullet at time 0 to always be at the center of the screen, but it behaves oddly. If i'm looking straight at the horizon to +- 20 degrees upward its fine but then it starts not following any more.
I set up my matrix like this:
void CCubeGame::SetCameraMatrix()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(Camera.rotx,1,0,0);
glRotatef(Camera.roty,0,1,0);
glRotatef(Camera.rotz,0,0,1);
glTranslatef(-Camera.x , -Camera.y,-Camera.z );
}
and change the angle like this:
void CCubeGame::MouseMove(int x, int y)
{
if(!isTrapped)
return;
int diffx = x-lastMouse.x;
int diffy = y-lastMouse.y;
lastMouse.x = x;
lastMouse.y = y;
Camera.rotx += (float) diffy * 0.2;
Camera.roty += (float) diffx * 0.2;
if(Camera.rotx > 90)
{
Camera.rotx = 90;
}
if(Camera.rotx < -90)
{
Camera.rotx = -90;
}
if(isTrapped)
if (fabs(ScreenDimensions.x/2 - x) > 1 || fabs(ScreenDimensions.y/2 - y) > 1) {
resetPointer();
}
}
You need to scale X and Z by cos(xradrot). (In other words, multiply by cos(xradrot)).
Imagine you're pointing straight down the Z axis but looking straight up. You don't want the bullet to shoot down the Z axis at all, this is why you need to scale it. (It's basically the same thing that you're doing between X and Z, but now doing it on the XZ vector and Y.)
pls.x += float(sin(yrotrad)*cos(xrotrad)) ;
pls.z -= float(cos(yrotrad)*cos(xrotrad)) ;
pls.y += float(sin(twopi - xrotrad));
Related
I have to move a ball in an angle in an open SFML and keep it within the window size (Like the DVD thing), but my current function makes it to the bottom and doesn't "bounce". It slides across the bottom and stops once it reaches the other corner. The initial position is (1,1)
void Bubble::updatePosition() {
if( isTopBottom() ){
do{
_x += .1;
_y += -.2;
}while( !isTopBottom() );
}
else if( isLeftRight() ){
do{
_x += -.1;
_y += .2;
}while( !isLeftRight() );
}
else{
_x += .1;
_y += .2;
}
_bubble.setPosition(_x, _y);
}
the isLeftRight, isTopBottom are bools that check if they have reached the edges
Simple Solution
Use velocities and manipulate those on collision; then, use the velocity to update the position.
Check each edge separately and decide on one relevant velocity component based on that.
e.g. (following your values)
// Positions:
float x = 1.f;
float y = 1.f;
// Velocities:
float vx = 0.1f;
float vy = 0.2f;
// ... then, inside loop:
// Check collisions (and adjust velocity):
if (x < 0.f)
vx = 0.1f;
else if (x > 640.f)
vx = -0.1f;
if (y < 0.f)
vy = 0.2f;
else if (y > 640.f)
vy = -0.2f;
// update position (still inside loop):
x += vx;
y += vy;
Cleaner Solution
This is the same as the simple solution above but, since you tagged SFML, you can use SFML vectors to keep the two components together. Also modified variable names to be more clear. Pulled out the size of the window and the velocity amounts from being hard-coded into the logic as well:
const sf::Vector2f windowSize(640.f, 640.f);
const sf::Vector2f velocityAmount(0.1f, 0.2f);
sf::Vector2f position(1.f, 1.f);
sf::Vector2f velocity = velocityAmount;
// ... then, inside loop:
// Check collisions (and adjust velocity):
if (position.x < 0.f)
velocity.x = velocityAmount.x;
else if (position.x > windowSize.x)
velocity.x = -velocityAmount.x;
if (position.y < 0.f)
velocity.y = velocityAmount.y;
else if (position.y > windowSize.y)
velocity.y = -velocityAmount.y;
// update position (still inside loop):
position += velocity;
You should notice that the velocity is the values that are added to the position on each iteration of the loop and that velocity does not change when it is not considered colliding with an edge.
Initial Problem
The initial problem you had is it always moves in the same direction (towards the bottom-right) if it is not hitting an edge. This means that it'll never be allowed to rise above the bottom edge (or away from right edge).
I'm supposed to write a code that simulates an inelastic particle bouncing within a wall and a semicircle (radius 30, center # (50,0). The particle loses 3% of its original velocity every time it hits a wall and/or the semicircle.
I'm also using graphics.h library
I have made most of the program already, but the particle does not properly bounce on the semicircle.
I used the reflection matrix:
\begin{bmatrix}
cos 2\theta & sin2\theta \
sin2 \theta & -cos2\theta
\end{bmatrix}
here is the this snippet of the code containing the conditions when the particle hits the semicircle:
//initial conditions
x = 0;
y = 100;
Vx = 15.0;
Vy = 0.0;
double dt = 0.0001; //initial time step and increment
for(double t = 0; t < 50; t += dt)
{
Vy = Vy + grav*dt;
//Conditions when the particle hits a wall or the floor and retaining only 97% of its speed
if (y<0.0)
{
Vy = -Vy + grav*dt;
}
if(x >100.0 || x < 0.0)
{
Vx = -Vx;
}
//condition when the particle hits the semicircle
//semicircle equation: y = sqrt(30^2 - (x-50)^2)
if(y < sqrt((30*30)-((x-50)*(x-50))) )
{
Vxnew = ((Vx)*(cos(2.0*theta)) + (Vy*(sin(2.0*theta))));
Vy = (Vy*(cos(2.0*theta)) - ((Vx)*(sin(2.0*theta))));
Vx = Vxnew;
}
x = x + Vx*dt;
y = y + Vy*dt;
putpixel(conx(x), cony(y), 15);
}
I'm not sure if this reflection matrix applies on curved surfaces and I should use something else or my implementation is wrong.
For the losing the velocity part, I can probably just multiply the new x and y velocities by 0.97.
For context, this function that I made converts the calculated value in the loop to pixel values
//convert to pixel value (scale of 6)
double conx(double x)
{
return x * (600/100) + 50;
}
double cony(double y)
{
return -y * (600/100) + 650;
}
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.
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.
I am trying to rotate opengl scene using track ball. The problem i am having is i am getting rotations opposite to direction of my swipe on screen. Here is the snippet of code.
prevPoint.y = viewPortHeight - prevPoint.y;
currentPoint.y = viewPortHeight - currentPoint.y;
prevPoint.x = prevPoint.x - centerx;
prevPoint.y = prevPoint.y - centery;
currentPoint.x = currentPoint.x - centerx;
currentPoint.y = currentPoint.y - centery;
double angle=0;
if (prevPoint.x == currentPoint.x && prevPoint.y == currentPoint.y) {
return;
}
double d, z, radius = viewPortHeight * 0.5;
if(viewPortWidth > viewPortHeight) {
radius = viewPortHeight * 0.5f;
} else {
radius = viewPortWidth * 0.5f;
}
d = (prevPoint.x * prevPoint.x + prevPoint.y * prevPoint.y);
if (d <= radius * radius * 0.5 ) { /* Inside sphere */
z = sqrt(radius*radius - d);
} else { /* On hyperbola */
z = (radius * radius * 0.5) / sqrt(d);
}
Vector refVector1(prevPoint.x,prevPoint.y,z);
refVector1.normalize();
d = (currentPoint.x * currentPoint.x + currentPoint.y * currentPoint.y);
if (d <= radius * radius * 0.5 ) { /* Inside sphere */
z = sqrt(radius*radius - d);
} else { /* On hyperbola */
z = (radius * radius * 0.5) / sqrt(d);
}
Vector refVector2(currentPoint.x,currentPoint.y,z);
refVector2.normalize();
Vector axisOfRotation = refVector1.cross(refVector2);
axisOfRotation.normalize();
angle = acos(refVector1*refVector2);
I recommend artificially setting prevPoint and currentPoint to (0,0) (0,1) and then stepping through the code (with a debugger or with your eyes) to see if each part makes sense to you, and the angle of rotation and axis at the end of the block are what you expect.
If they are what you expect, then I'm guessing the error is in the logic that occurs after that. i.e. you then take the angle and axis and convert them to a matrix which gets multiplied to move the model. A number of convention choices happen in this pipeline --which if swapped can lead to the type of bug you're having:
Whether the formula assumes the angle is winding left or right handedly around the axis.
Whether the transformation is meant to rotate an object in the world or meant to rotate the camera.
Whether the matrix is meant to operate by multiplication on the left or right.
Whether rows or columns of matrices are contiguous in memory.