Rotation matrix - rotate a ball around a rotating box - c++

I've a 3D box: center point = (a,b,c), width = w, height = h, depth = d.
the center point isn't the origin.
I have a ball on the box(touch each other), its center and radius.
I can rotate the box(around the X axis but its center STAYS the same..) and I want the ball to stay stuck to the box. so the ball needs to be rotated WITH the box.
the angle of the rotation is 45 degrees.
I tried to do this:
I defined the Rotation Matrix around the X axis:
mat[3][3]
1, 0 , 0
0, cos(45), -sin(45)
0, sin(45), cos(45)
and multiply it by the ball center vector:
(ball.Center().m_x , ball.Center().m_y, ball.Center().m_z) * mat
so I got:
Point3D new_center(ball.Center().m_x,
ball.Center().m_y*cos(45) + ball.Center().m_z*sin(45),
-(ball.Center().m_y)*sin(45) + ball.Center().m_z*cos(45));
ball.Center() = new_center;
the ball is really rotated when the box is rotated but too far. How can I fix it?

Have you tried to translate it to the origin of coordinates, rotate and then translate back?
And I think that the coordinates should be multiplied on the right by the transformation matrix, i.e.:
Point3D new_center(ball.Center().m_x,
ball.Center().m_y*cos(45) - ball.Center().m_z*sin(45),
ball.Center().m_y*sin(45) + ball.Center().m_z*cos(45);
ball.Center() = new_center;

thanks to Alexander Mihailov, here's the final answer:
// correcting the ball center to the origin according the box.Center
Point3D ball_center_corrected = ball.Center() - box.Center();
// rotation_matrix(of X axis) * ball_center_corrected
// so the rotation is around the X axis
Point3D new_center(ball_center_corrected.m_x,
ball_center_corrected.m_y*cos(angle) -
ball_center_corrected.m_z*sin(angle),
ball_center_corrected.m_y*sin(angle) +
ball_center_corrected.m_z*cos(angle));
// translate the ball center back around the box
ball.Center() = new_center + box.Center();

Related

Drawing a sprite on the circumference of a circle based on the position of other objects

I'm making a sniper shooter arcade style game in Gamemaker Studio 2 and I want the position of targets outside of the viewport to be pointed to by chevrons that move along the circumference of the scope when it moves. I am using trig techniques to determine the coordinates but the chevron is jumping around and doesn't seem to be pointing to the target. I have the code broken into two: the code to determine the coordinates in the step event of the enemies class (the objects that will be pointed to) and a draw event in the same class. Additionally, when I try to rotate the chevron so it also points to the enemy, it doesn't draw at all.
Here's the coordinate algorithm and the code to draw the chevrons, respectively
//determine the angle the target makes with the player
delta_x = abs(ObjectPlayer.x - x); //x axis displacement
delta_y = abs(ObjectPlayer.y - y); //y axis displacement
angle = arctan2(delta_y,delta_x); //angle in radians
angle *= 180/pi //angle in radians
//Determine the direction based on the larger dimension and
largest_distance = max(x,y);
plusOrMinus = (largest_distance == x)?
sign(ObjectPlayer.x-x) : sign(ObjectPlayer.y-y);
//define the chevron coordinates
chevron_x = ObjectPlayer.x + plusOrMinus*(cos(angle) + 20);
chevron_y = ObjectPlayer.y + plusOrMinus*(sign(angle) + 20);
The drawing code
if(object_exists(ObjectEnemy)){
draw_text(ObjectPlayer.x, ObjectPlayer.y-10,string(angle));
draw_sprite(Spr_Chevron,-1,chevron_x,chevron_y);
//sSpr_Chevron.image_angle = angle;
}
Your current code is slightly more complex that it needs to be for this, if you want to draw chevrons pointing towards all enemies, you might as well do that on spot in Draw. And use degree-based functions if you're going to need degrees for drawing anyway
var px = ObjectPlayer.x;
var py = ObjectPlayer.y;
with (ObjectEnemy) {
var angle = point_direction(px, py, x, y);
var chevron_x = px + lengthdir_x(20, angle);
var chevron_y = py + lengthdir_y(20, angle);
draw_sprite_ext(Spr_Chevron, -1, chevron_x, chevron_y, 1, 1, angle, c_white, 1);
}
(also see: an almost-decade old blog post of mine about doing this while clamping to screen edges instead)
Specific problems with your existing code are:
Using a single-axis plusOrMinus with two axes
Adding 20 to sine/cosine instead of multiplying them by it
Trying to apply an angle to sSpr_Chevron (?) instead of using draw_sprite_ext to draw a rotated sprite.
Calculating largest_distance based on executing instance's X/Y instead of delta X/Y.

Rotating a RigidBody around a pivot point

I'm trying to rotate a rigidbody around a pivot point (in this case the origin), rather than its center of mass.
I had a suggestion to apply three transformations:
Transform the rigidbody to the origin
Rotate the rigidbody on its center of mass
Transform the rigidbody off of the origin.
Here is my code:
btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();
btQuaternion quat;
orn.getRotation(quat);
btVector3 axis = quat.getAxis();
//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btVector3(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ()));
//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);
//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();
//Move the rigidbody 2 units along new axis
btPhys->translate(btVector3(2.0 * axis.getX(), 0.0, 2.0 * axis.getZ()));
However, the pivot points appears to be moving around instead of staying in one place (the origin). Is there a better way (that actually works) to rotate a rigidbody around a pivot point?
EDIT:
I added some sanity-check code for the rotate function:
//Code that doesn't work
btVector3 invTrans = btPhys->offsetToPivot.rotate(btVector3(1.0, 0.0, 0.0), btScalar(degreesToRads(-1)));
//Values printed out are identical to offsetToPivot
printf("invTrans: %f %f %f\n", invTrans.getX(), invTrans.getY(), invTrans.getZ());
//Sanity code that DOES work
//Arbitrary vector
btVector3 temp = btVector3(0.0, 2.0, 0.0);
temp = temp.rotate(btVector3(1.0, 0.0, 0.0), btScalar(degreesToRads(-1)));
printf("temp %f %f %f\n", temp.getX(), temp.getY(), temp.getZ());
This method actually works, you're just applying it incorrectly. Your second translation is performed along world axis but you have rotated the object, so you have to translate it back along the rotated vector.
Correct code should look more or less like this:
btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();
btQuaternion quat;
orn.getRotation(quat);
btVector3 axis = quat.getAxis();
//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btVector3(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ()));
//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);
//Get rotation matrix
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(degreesToRads(-1))),btVector3(0,0,0));
//Rotate your first translation vector with the matrix
btVector3 invTrans(-2.0 * axis.getX(), 0.0, -2.0 * axis.getZ());
invTrans = invRot * invTrans;
//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();
//Translate back by rotated vector
btPhys->translate(-invTrans);
I'm not sure if the rotation shouldn't be with minus (I can't check it right now) but you can easily try both.
EDIT.
Ok, so you forgot to mention that you perform a continuous rotation instead of a single one. This procedure is correct for a single rotation around pivot (eg. 30 degrees rotation). I've looked into your code once more and I understand that you try to perform your first translation along local x and z-axis. However it is not what happens. In this line:
btVector3 axis = quat.getAxis();
the variable axis is a unit vector representing the axis around which your object is rotated. It is NOT its coordinate system. I haven't noticed this part before. Quaternions are tricky and you should read more about them because many people missuse them.
A solution that will work in a continuous case is to store the last translation (from center of mass to pivot - in my example it is represented by invTrans) in your object and use it to perform the first translation, then rotate it in the same way it is done, and use it to move to the right position.
The corrected code will look like this:
btMatrix3x3 orn = btPhys->getWorldTransform().getBasis();
btQuaternion quat;
orn.getRotation(quat);
//Move rigidbody 2 units along its axis to the origin
btPhys->translate(btPhys->offsetToPivot);
//Rotate the rigidbody by 1 degree on its center of mass
orn *= btMatrix3x3(btQuaternion( btVector3(1, 0, 0), btScalar(degreesToRads(-1))));
btPhys->getWorldTransform().setBasis(orn);
//Get rotation matrix
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(degreesToRads(-1))),btVector3(0,0,0));
//Rotate your first translation vector with the matrix
btVector3 invTrans = invRot * btPhys->offsetToPivot;
//Update axis variable to apply transform on
orn.getRotation(quat);
axis = quat.getAxis();
//Translate back by rotated vector
btPhys->translate(-invTrans);
btPhys->offsetToPivot = invTrans;
However before starting this whole procedure you have to set offsetToPivot into its position relative to the center of mass.
I have an impression that the main source of your problems is the lack of understanding of linear algebra and basic spatial transformations. If you are planning to continue in this field, I strongly recommend reading into this topic. Also drawing your problem on paper really helps.
EDIT2.
Ok, I've tried your code:
btVector3 temp = vec3(0,2,0);
btTransform invRot(btQuaternion(btVector3(1, 0, 0), btScalar(-0.017453f)),btVector3(0,0,0));
temp = invRot * temp;
After this, temp is equal to {0.000000000, 1.99969542, -0.0349042267}.
In the below function, these transformations perform the three steps you've described:
int x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
int y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
namely:
Step 1:Transform the rigidbody to the origin.
initial.x - axisOfRotation.x
initial.y - axisOfRotation.y
Step 2:Rotate the rigidbody on its center of mass.
cos(angRads) * initial.x - sin(angRads) * initial.y
sin(angRads) * initial.x + cos(angRads) * initial.y
Step 3:Transform the rigidbody off of the origin.
+axisOfRotation.x;
+axisOfRotation.y;
Here is a recursive function that performs exactly what you need and returns
all the consecutively rotated points in a vector: (use it as a benchmark)
rotateCoordinate(vector<Point>& rotated, Point& axisOfRotation, Point initial,
float angRads, int numberOfRotations){
// base case: when all rotations performed return vector holding the rotated points
if(numberOfRotations <= 0) return;
else{
// apply transformation on the initial point
int x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
int y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
// save the result
rotated.push_back(Point(x, y));
// call the same function this time on the rotated point and decremented number of rotations
rotateCoordinate(rotated, axisOfRotation, Point(x,y), angRads, numberOfRotations -1);
}
}
where Point is:
struct Point {
int x, y;
Point(int xx, int yy) : x(xx), y(yy) { }
Point() :x(0), y(0) { }
};
For further reading that explains the math behind it here.

How to rotate points about a specific origin?

I'm working on rotating the vertices of my object around a point located on the object that's not necessarily its center. I followed this tutorial pretty closely and got the vertices to keep their same proportions, i.e. the shape that they create does rotate about the given point, however the amount by which it rotates seems arbitrary. I'll explain in the code and the screenshots. I'm using SFML, but I'll explain the sf:: namespaces in the comments where they're used for those who need it. Anyways, here's my main file that shows the problem:
int _tmain(int argc, _TCHAR* argv[]){
sf::RenderWindow window(sf::VideoMode(500, 500, 32), "Animation");
//sf::vertexarray is a list of POINTS on the screen, their position is determined with a sf::vector
sf::VertexArray vertices;
//arrange 6 points in a shape
vertices.setPrimitiveType(sf::PrimitiveType::Points);
//bottom middle
vertices.append(sf::Vector2f(200, 200));
//left bottom edge
vertices.append(sf::Vertex(sf::Vector2f(195, 195)));
//left top edge
vertices.append(sf::Vertex(sf::Vector2f(195, 175)));
//top middle
vertices.append(sf::Vertex(sf::Vector2f(200, 170)));
//top right corner
vertices.append(sf::Vertex(sf::Vector2f(205, 175)));
//bottom right corner
vertices.append(sf::Vertex(sf::Vector2f(205, 195)));
//rotation is the shape's rotation... 0 means it's straight up, and it rotates clockwise with positive rotation
float rotation = 0;
//used later to pause the program
bool keypressed = false;
while(window.isOpen()){
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)){
window.close();
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)){
//this SHOULD rotate the shape by 10 degrees, however it rotates it by like 150-ish
//why does this not work as expected?
rotation = 10;
//this transformation part works fine, it simply moves the points to center them on the origin, rotates them using a rotation matrix, and moves
//them back by their offset
for(int i = 1; i < vertices.getVertexCount(); i++){
//translate current point so that the origin is centered
vertices[i].position -= vertices[0].position;
//rotate points
//I'm guessing this is the part where the rotation value is screwing up?
//because rotation is simply theta in a regular trig function, so the shape should only rotate 10 degrees
float newPosX = vertices[i].position.x * cosf(rotation) + vertices[i].position.y * sinf(rotation);
float newPosY = -vertices[i].position.x * sinf(rotation) + vertices[i].position.y * cosf(rotation);
//translate points back
vertices[i].position.x = newPosX + vertices[0].position.x;
vertices[i].position.y = newPosY + vertices[0].position.y;
}
keypressed = true;
}
//draw
window.clear();
window.draw(vertices);
window.display();
if(keypressed == true){
//breakpoint here so the points only rotate once
system("pause");
}
}
return 0;
}
Also, here are the screenshots showing what I mean. Sorry it's a bit small. The left side shows the shape created at the start of the program, with the green point being the origin. The right side shows the shape after the rotation for loop is called, with the red points showing where the shape actually rotates to (definitely not 10 degrees) versus the blue dots, which are roughly where I expected the shape to be at, around 10 degrees.
tl;dr: Using a rotation matrix, the points being rotated keep their proportions, but the amount by which they are rotating is totally arbitrary. Any suggestions/improvements?
Using the SFML, you first create a transformation :
sf::Transform rotation;
rotation.rotate(10, centerOfRotationX, centerOfRotationY);
Then you apply this transformation to the position of each vertex :
sf::Vector2f positionAfterRotation = rotation.transformPoint(positionBeforeRotation);
Sources : sf::Transform::rotate and sf::Transform::transformPoint.

Free Flight Camera - strange rotation around X-axis

So I nearly implemented a free-flight camera using vectors and something like gluLookAt.
The movement in all 4 directions and rotation around the Y-axis work fine.
For the rotation around the Y-axis I calculate the vector between the eye and center vector and then rotate it with the rotation matrix like this:
Vector temp = vecmath.vector(center.x() - eye.x(),
center.y() - eye.y(), center.z() - eye.z());
float vecX = (temp.x()*(float) Math.cos(-turnSpeed)) + (temp.z()* (float)Math.sin(-turnSpeed));
float vecY = temp.y();
float vecZ = (temp.x()*(float) -Math.sin(-turnSpeed))+ (temp.z()*(float)Math.cos(-turnSpeed));
center = vecmath.vector(vecX, vecY, vecZ);
At the end I just set center to the newly calculated vector.
Now when I try to do the same thing for rotation around the X-axis it DOES rotate the vector but in a very strange way, kind of like it would be moving in a wavy line.
I use the same logic as for the previous rotation, just with the x rotation matrix:
Vector temp = vecmath.vector(center.x() - eye.x(),
center.y() - eye.y(), center.z() - eye.z());
float vecX = temp.x();
float vecY = (temp.y()*(float) Math.cos(turnSpeed)) + (temp.z()* (float)-Math.sin(turnSpeed));
float vecZ = (temp.y()*(float) Math.sin(turnSpeed)) + (temp.z()*(float)Math.cos(turnSpeed));
center = vecmath.vector(vecX, vecY, vecZ);
But why does this not work? Maybe I do something else somewhere wrong?
The problem you're facing is the exact same that I had trouble with the first time I tried to implement the camera movement. The problem occurs because if you first turn so that you are looking straight down the X axis and then try to "tilt" the camera by rotating around the X axis, you will effectively actually spin around the direction you are looking.
I find that the best way to handle camera movement is to accumulate the angles in separate variables and every time rotate completely from origin. If you do this you can first "tilt" by rotating around the X-axis then turn by rotating around the Y-axis. By doing it in this order you make sure that the tilting will always be around the correct axis relative to the camera. Something like this:
public void pan(float turnSpeed)
{
totalPan += turnSpeed;
updateOrientation();
}
public void tilt(float turnSpeed)
{
totalTilt += turnSpeed;
updateOrientation();
}
private void updateOrientation()
{
float afterTiltX = 0.0f; // Not used. Only to make things clearer
float afterTiltY = (float) Math.sin(totalTilt));
float afterTiltZ = (float) Math.cos(totalTilt));
float vecX = (float)Math.sin(totalPan) * afterTiltZ;
float vecY = afterTiltY;
float vecZ = (float)Math.cos(totalPan) * afterTiltZ;
center = eye + vecmath.vector(vecX, vecY, vecZ);
}
I don't know if the syntax is completely correct. Haven't programmed in java in a while.

Rotating CCSprite thats an equilateral triangle

I have an CCSprite thats an equilateral triangle. I want to rotate the triangle in 60 degree increments holding its position.
The sprite is 126x110 (not square) setting the sprites rotation property in 60 degree increments changes the position of the sprite. How can i keep the sprite appear stationary at each rotation?
A bit more about this the center of the rectangle IS NOT the center of the sprite. So there is some adjustment needed when the rotation is needed to keep the center of the triangle appearing centered.
I think i came up with a long answer that needs to be approved..
// collect points.
self.point1 = CGPointMake(CGRectGetWidth(self.tile.boundingBox)/2, 0);
self.point2 = CGPointMake(CGRectGetWidth(self.tile.boundingBox), CGRectGetHeight(self.tile.boundingBox));
self.point3 = CGPointMake(0, CGRectGetHeight(self.tile.boundingBox));
// calculcate the mid point.
float midPointX = floor((self.point1.x + self.point2.x + self.point3.x)/3.0);
float midPointY = floor((self.point1.y + self.point2.y + self.point3.y)/3.0);
// stash the center of the triangle
self.triangleCenter = CGPointMake(midPointX, midPointY);
Then figure out new location of the center point based on rotation.. And animate there. (Sorry about hard coded center of the screen this was a rough test).
-(void) rotateToAngleAboutCenter:(float)angle {
// stash old values.
CGPoint oldPosition = self.tile.position;
float oldRotation = self.tile.rotation;
// reset the rotation
self.tile.rotation = 0;
// this is hard coded here currently the center of the screen.
self.tile.position = ccp(512, 384);
// figure out where our center point will be when we are rotated about the center.
CGPoint point = ccpRotateByAngle(self.triangleCenter, [self.tile anchorPointInPoints],-CC_DEGREES_TO_RADIANS(angle));
// convert the ppoint to local space.
point = [self.tile convertToWorldSpace:point];
point = [self convertToNodeSpace:point];
// reset the rotation and position.
self.tile.rotation = oldRotation;
self.tile.position = oldPosition;
// animate to new rotation/position.
CCMoveTo* moveTo = [CCMoveTo actionWithDuration:.25 position:point];
CCRotateTo* rotateTo = [CCRotateTo actionWithDuration:.25 angle:angle];
[self.tile runAction:moveTo];
[self.tile runAction:rotateTo];
}