In my 3d application I wish to have an object (a tree, for example), and my camera to look at this object. Then, I want the camera to rotate about the object, in a circle, while looking at the tree the whole time. Imagine walking around a tree, while constantly changing your angle so that you are still looking at it. I know this requires both rotation of my camera, and translation of my camera, but the math is far beyond the level I have been taught in schooling thusfar. Can anyone point me in the right direction?
Here is one way with very simple math. First, you need a constant for the distance the camera is from the center of the tree (the radius of the circle path it travels on). Also, you need some variable to track it's angle around the circle.
static final float CAM_PATH_RADIUS = 5f;
static final float CAM_HEIGHT = 2f;
float camPathAngle = 0;
Now you can change the camPathAngle to anything you want from 0 to 360 degrees. 0 degrees corresponds with the location on the circle that is in the same direction as the world's X-axis from the tree's center.
On each frame, after you've update camPathAngle, you can do this to update the camera position.
void updateTreeCamera(){
Vector3 camPosition = camera.getPosition();
camPosition.set(CAM_PATH_RADIUS, CAM_HEIGHT, 0); //Move camera to default location on circle centered at origin
camPosition.rotate(Vector3.Y, camPathAngle); //Rotate the position to the angle you want. Rotating this vector about the Y axis is like walking along the circle in a counter-clockwise direction.
camPosition.add(treeCenterPosition); //translate the circle from origin to tree center
camera.up.set(Vector3.Y); //Make sure camera is still upright, in case a previous calculation caused it to roll or pitch
camera.lookAt(treeCenterPosition);
camera.update(); //Register the changes to the camera position and direction
}
I did it like that for the sake of commenting it. It's actually shorter than the above if you chain commands:
void updateTreeCamera(){
camera.getPosition().set(CAM_PATH_RADIUS, CAM_HEIGHT, 0)
.rotate(Vector3.Y, camPathAngle).add(treeCenterPosition);
camera.up.set(Vector3.Y);
camera.lookAt(treeCenterPosition);
camera.update();
}
Related
I have not found answer for this question anywhere, so let's go.
What i expect:
I want to render rocket. Rocket is flying from given start point with evaluated angle. I'm evaluating angle like this:
getVelocity().angle() - 90f
My problem is to calibrate rocket position on top of the rocket. Image below shows how should it work:
In the top picture is how libgdx render not rotated texture region. In the bottom picture is what i expect: I want to move and rotate texture region with given angle to have (x,y) coordinate on the top of rocket.
What i have:
I tired to write method to draw sprite how i expect but i failed. I think it is caused due to fact that i don't understand documentation of this method.
Following manual:
void com.badlogic.gdx.graphics.g2d.SpriteBatch.draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float rotation)
Draws a rectangle with the bottom left corner at x,y and stretching the region to cover the given width and height. The rectangle is offset by originX, originY relative to the origin. Scale specifies the scaling factor by which the rectangle should be scaled around originX, originY. Rotation specifies the angle of counter clockwise rotation of the rectangle around originX, originY.
My code:
public static void drawRotatedTex(SpriteBatch pmRenderer, TextureRegion pmTex, float pmPosX, float pmPosY, float pmAngle)
{
pmRenderer.begin();
pmRenderer.draw(
pmTex, pmPosX, pmPosY, -pmTex.getRegionWidth()/2, pmTex.getRegionHeight(), pmTex.getRegionWidth(), pmTex.getRegionHeight(), 1f, 1f, pmAngle);
pmRenderer.end();
}
Results:
It is moment of collision. As we can see coordinates are offset in relation to rocket.
I don't ask about full solution. For me will be sufficient if someone explain me (on drawing or something) like this method works.
EDIT
Moderation suggested that this question is duplicate of:
libgdx: Rotate a texture when drawing it with spritebatch
I read this topic, but it is not my solution. I know how to rotate my sprite by i don't have idea why coordinates of rocket are offset in relation to rocket top.
EDIT
Invocation of my drawRotatedTex from rocket class:
#Override
public void render(Renderer pmRenderer, float pmX, float pmY) {
SpriteBatch lvSpritebatch = pmRenderer.getSpriteBatch();
Sprite lvSprite = null;
if(mIsExploding)
{
if((lvSprite = mExplosion.getTexture()) != null)
{
lvSpritebatch.begin();
lvSpritebatch.draw(lvSprite, pmX + lvSprite.getWidth()/2, pmY - lvSprite.getHeight()/2);
lvSpritebatch.end();
}
}
else
{
lvSprite = mAnimation.getTexture();
RendererTools.drawRotatedTex(lvSpritebatch,lvSprite,pmX,pmY,getVelocity().angle() - 90f);
}
}
It is not very clear what you are asking, if it's only about moving the origin you would not need that much text. Anyway, I will take a shot at it.
If you want to accomplish what you have in your picture you setup your sprite like this:
sprite.setOrigin(s.getWidth()/2, s.getHeight()); //Centers sprite on top of image.
If I now rotate it in the draw method it rotates around the top center.
sprite.rotate(5);
sprite.draw(batch);
Despite being rotated around the center top of itself it remains position remains the same. If I would set the origin far away from the image and rotate it then the image would travel very far but the position remains the same. Still if you would move it's position 10 pixels to the right the image (wherever it may be) will be moved to the right.
Hope this helps.
I'm working on 3D tower defence and I need some help with rotation of a ModelInstance.
How to rotate the ModelInstance to the second one with a coefficient of rotation speed.
For example: rotate base(or weapon) to enemy
Thanks for help.
Actually you should never rotate a ModelInstance to face another ModelInstance, but you should rotate it to an angle given by the gamelogic.
That means, that you should sepperate the logic and the view.
In this case the logic needs to store the currentAngle and the desiredAngle, as well as a rotationSpeed.
The desiredAngle can be calculated out of the positions of the Tower and the Enemy.
In the update(delta) you can then say:
if (desiredAngle > currentAngle)
currentAngle+=rotationSpeed*delta;
The same for the negative rotation and ensure to keep rotation between 0 and 360°.
You can then rotate the ModelInstance to the given currentAngle by rotating its Matrix4 transform, i gues the rotate(float axisX, float axisY, float axisZ, float degrees) should be the rigth one.
I have a Ball in 3D space that has the following attributes:
location - a Vector3f representing where the ball is
rotation - a Vector3f representing the x, y, and z axis rotation angles
I want to roll the ball in a particular direction denoted by a Vector3f "direction". How would I calculate the appropriate axis rotation vector (see above) based on the direction I want the ball to roll towards?
I've tried the following:
set rotation.x to direction.z
set rotation.z to direction.x
Calculate the ball's transform matrix as:
private Matrix4f calculateEntityMatrix(EEntity entity)
{
Matrix4f matrix = new Matrix4f();
matrix.translate(new Vector3f(entity.getXLocation(), entity.getYLocation(), entity.getZLocation()));
if(entity.getXRotation()>0)
{
matrix = matrix.rotate(entity.getXRotation(), new Vector3f(1f, 0f, 0f));
}
if(entity.getYRotation()>0)
{
matrix = matrix.rotate(entity.getYRotation(), new Vector3f(0f, 1f, 0f));
}
if(entity.getZRotation()>0)
{
matrix = matrix.rotate(entity.getZRotation(), new Vector3f(0f, 0f, 1f));
}
if(entity.getXScale()!=1 || entity.getYScale()!=1 || entity.getZScale()!=1)
{
matrix = matrix.scale(new Vector3f(entity.getXScale(), entity.getYScale(), entity.getZScale()));
}
return matrix;
}
This works when rolling down either the x or z axis, but when I roll in a direction between the two axes the rotation appears incorrect. My assumption is that this is caused by the fact that the rotation is being calculated as follows:
the ball is rotated by rotation.x along the X axis
the ball is then rotation by rotation.z along "new" X axis created by step 1.
Any suggestions how this behaviour could be changed so that each rotation is calculated independently of each other?
Unless you want to implement slipping and/or backspin, I think you should approach this problem slightly different. You already have a Matrix.rotate() that supports rotation around an arbitrary axis, use it.
Attributes to store for the ball
position
rotation matrix
Note: in a matrix-oriented system / scene graph, bould would usually be stored in a single 4x4 transformation matrix. That might or might not be more convenience, depending on the rest of your current code base.
Algorithm for moving around
Given the ball direction and the standard up vector (0, 1, 0), calculate the rotation axis using the cross product. (i.e. it's perpendiciular to both direction and up axis)
Rotation is now simply a matrix.rotate( rotationSpeed, rotationAxis), applied to the existing rotation matrix.
If the ball is allowed to roll up/down surfaces, replace the standard up vector with the appropriate surface normal.
I have a 3D arrow drawn with OpenGL that points to the coordinates (0, 0, 0) and I want it to point to a specific GPS location depending on my GPS position and Orientation.
I've tried calculating the azimuth (with my phone's orientation) and adjusting it to be the real north (not the magnetic north).
SensorManager.getOrientation(remappedRotationMatrix, orientation);
// convert radians to degrees
float azimuth = orientation[0];
azimuth = azimuth * 360 / (2 * (float) Math.PI);
GeomagneticField geoField = new GeomagneticField(
Double.valueOf(loc.getLatitude()).floatValue(),
Double.valueOf(loc.getLongitude()).floatValue(),
Double.valueOf(loc.getAltitude()).floatValue(),
System.currentTimeMillis());
// converts magnetic north into true north
azimuth -= geoField.getDeclination();
Then getting the bearing from my Location to the Location I want to point.
target.setLatitude(42.806484);
target.setLongitude(-1.632482);
float bearing = loc.bearingTo(target); // (it's already in degrees)
if (bearing < 0) {
bearing = bearing + 360;
}
float degrees = bearing - azimuth;
if (degrees < 0) {
degrees = degrees + 360;
}
and calculating the degrees I have to rotate the arrow
gl.glRotatef(degrees, 0.0f, 1.0f, 0.0f);
arrow.draw(gl);
Is there someway to do it? Could another possibility be to convert the GPS position to the OpenGL coordinates and use GLU.gluLookAt to point to it?
Thanks.
This seem to be purely a math problem.
Your question is pretty vague, I don't think I can help you without understanding more precisely how your scene is set up and what you want.
Do you know how to use 3D rotation matrices? If not, you probably should learn how they work.
It shouldn't be complicated to calculate the bearing and then rotate the arrow by the degrees you get. I have done the same in 2D although not in OpenGL. I based my code on the Radar sample (http://apps-for-android.googlecode.com/svn/trunk/Radar/). Here is how I draw the 2D arrow:
double bearingToTarget = mBearing - mOrientation;
// Draw an arrow in direction of target
canvas.rotate((float) bearingToTarget, center, center);
final int tipX = center;
final int tipY = center-radius;
canvas.drawLine(center, center, tipX, tipY, mArrowPaint);
final int tipLen = 30;
final int tipWidth = 20;
Path path = new Path();
path.moveTo(tipX, tipY);
path.lineTo(tipX + tipWidth/2, tipY + tipLen);
path.lineTo(tipX - tipWidth/2, tipY + tipLen);
path.lineTo(tipX, tipY);
path.close();
canvas.drawPath(path, mArrowPaint);
canvas.restore();
mBearing is calculated using the method GeoUtils.bearing from the Radar sample which takes care of the complicated math. mOrientation is just the orientation from the sensor listener. So the idea is to compute the difference between the bearing of the GPS location you want to point to (mBearing) and the current orientation of the phone (mOrientation). This gives us the angle bearingToTarget. We then rotate the view about its center by that angle before drawing the arrow along the y axis. This is the same as drawing the arrow rotated by bearingToTarget degrees.
You should be able to apply the same logic in OpenGL by rotating the view about the center of the screen by bearingToTarget degrees before you draw the arrow. Exactly what point you rotate about depends on how your view is set up. To make it simple, make the starting point of your arrow at the origin. Then you can simply rotate about the origin using glRotatef. Otherwise you would first need to translate to the center of the rotation, rotate and then translate back again (this is the common OpenGL technique for rotation about a point).
The issue involves an Android Path shape. It's a triangle that I'm using as an arrow to point towards objects on a screen Canvas. This is for a 2d game. player in the middle of the screen, objects around him and offscreen.
These arrows are supposed to rotate around the center of the screen, with a radius so that they rotate in a circle around the player. The arrows point towards objects that the player needs to move towards.
What I have right now is somewhat working, but the arrows are zipping around the circle at ridiculous speeds. Funny enough, they're pointing in the right direction, but they aren't staying at the right point on the circle. (if arrow is pointing northeast, arrow should be on the northeast part of the circle, etc)
I'm sure it's because of the math. I'm probably using atan2 wrong. Or canvas.translate wrong. Or maybe I shouldn't be using atan2 at all. Help! :)
Here is the code:
// set the shape of our radar blips
oBlipPath.moveTo(0, -5);
oBlipPath.lineTo(5, 0);
oBlipPath.lineTo(0, 5);
// Paint all the enemies and radar blips!
for(int i=0; i<iNumEnemies; i++){
if (oEnemies[i].draw(canvas, (int)worldX, (int)worldY)){
//calculate the degree the object is from the center of the screen.
//(user is the player. this could be done easier using iWidth and iHeight probably)
//we use a world coordinate system. worldY and worldX are subtracted
fDegrees = (float)Math.toDegrees(Math.atan2((oEnemies[i].getEnemyCenterY()-worldY)-user.getShipCenterY(), (oEnemies[i].getEnemyCenterX()-worldX)-user.getShipCenterX()));
canvas.save();
//get to the center
canvas.translate((iWidth / 2) , (iHeight / 2) );
//move a little bit depending on direction (trying to make arrows appear around a circle)
canvas.translate((float)(20 * Math.cos(fDegrees)), (float)(20* Math.sin(fDegrees)));
//rotate canvas so arrows will rotate and point in the right direction
canvas.rotate(fDegrees);
//draw arrows
canvas.drawPath(oBlipPath, oBlipPaint);
canvas.restore();
}
}
Affine transformations are are not commutative. They are typically applied in an apparent last-specified-first-applied order. As an alternative, consider the rotate() variation that rotates about a point.
Well, I've got it doing what I wanted, but I don't really know how. I threw in some random numbers until things showed up on the screen the way I wanted. If anyone wants to clue me in as to a better way to do this, I'm all ears.
The code:
// set the shape of our radar blips
oBlipPath.moveTo(0, -5);
oBlipPath.lineTo(6, 0);
oBlipPath.lineTo(0, 5);
oBlipMatrix.setRotate(45, 0, 0);
oBlipPath.transform(oBlipMatrix);
// Paint all the enemies and radar blips!
for(int i=0; i<iNumEnemies; i++){
oEnemies[i].draw(canvas, (int)worldX, (int)worldY);
if (oEnemies[i].bActive){
//calculate the degree the object is from the center of the screen.
//(user is the player. this could be done easier using iWidth and iHeight probably)
//we use a world coordinate system. worldY and worldX are subtracted
fDegrees = (float)Math.toDegrees(Math.atan2((oEnemies[i].getEnemyCenterY()-worldY)-(iHeight / 2), (oEnemies[i].getEnemyCenterX()-worldX)-(iWidth / 2)));
canvas.save();
//get to the center
canvas.translate((iWidth / 2 + 50) , (iHeight / 2 + 50) );
//move a little bit depending on direction (trying to make arrows appear around a circle)
//canvas.translate((float)(20 * Math.cos(fDegrees)), (float)(20* Math.sin(fDegrees)));
//rotate canvas so arrows will rotate and point in the right direction
canvas.rotate(fDegrees-45, -50, -50);
//draw arrows
canvas.drawPath(oBlipPath, oBlipPaint);
canvas.restore();
}
}
For whatever reason, I have to subtract 45 degrees from the canvas rotation, but add 45 degrees to the matrix rotation of the path shape. It works, but why?! :)