I am trying to move a triangle in the direction of the top vertex.
Depending on the rotation angle.
This is my code:
private static void render() {
// Clear the pixels on the screen and clear the contents of the depth buffer (3D contents of the scene)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset any translations the camera made last frame update
glLoadIdentity();
// Apply the camera position and orientation to the scene
//camera.applyTranslations();
glTranslated(0,0,-5);
glPushMatrix();
glRotated(f.get_direction(),0,0,1);
glTranslated(x,y,0);
f.draw();
glPopMatrix();
x+=(f.get_speed()/30)*cos(f.get_direction()+90);
y+=(f.get_speed()/30)*sin(f.get_direction()+90);
}
The point is that no matter what is the rotation angle that is the direction,
i want to move the triangle according to it.
Did you try to translate according to direction vector but there is simple problem cos and sin arguments is in radians glRotate in degrees:
so we must create static function
static double degToRad(double x)
{
return (x / 180.0) * Math.PI;
}
than use it
glLoadIdenity();
x += Math.cos(degToRad(getDirection() + 90)) * getSpeed();
y += Math.sin(degToRad(getDirection() + 90)) * getSpeed();
glTranslatef(x, y, 0);
glRotatef(getDirection(), 0, 0, 1);
drawObject();
you can also invert direction of movement just subsctacting current angle from 360:
glLoadIdenity();
x += Math.cos(degToRad(360 - getDirection() + 90)) * getSpeed();
y += Math.sin(degToRad(360 - getDirection() + 90)) * getSpeed();
glTranslatef(x, y, 0);
glRotatef(getDirection(), 0, 0, 1);
drawObject();
Related
I have an image that I rotate before I draw. The image is rotated by the angles of a hexagon. In other words, the image basically "highlights" the individual edges of a hexagon. I need to detect if the mouse was clicked inside of this rotated image. Detecting a mouse click inside of an unrotated image is pretty simple, but I have no idea about how to detect clicks within rotated points. Is there a way to get the points of the image's corners after rotation so I can place an invisible polygon on top of the image and use Polygon.contains()?
Image highlightEdge = new Image("assets/img/highlightEdge.png");
if(angle == 90){
highlightEdge.setCenterOfRotation(highlightEdge.getWidth(), 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(testPoint.x - 56, testPoint.y);
} else if(angle == 210) {
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x - 72, lastSettlement.y - 32);
} else if( angle == 330){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x - 8, lastSettlement.y - 32);
} else if(angle == 30){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-8, lastSettlement.y);
} else if(angle == 150){
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-72, lastSettlement.y);
} else {
highlightEdge.setCenterOfRotation(0, 0);
highlightEdge.rotate(new Float(angle));
highlightEdge.draw(lastSettlement.x-40, lastSettlement.y - 48);
}
You could create a Shape to exactly match the shape of the Image, and then use its method contains to detect if the mouse was clicked inside.
To take in consideration the rotation of the Image you could apply a corresponding rotation Transform to the Shape.
I created the method shapeFromImage that does this; it receives an Image and its position and returns the corresponding Shape:
/**
* Returns the Shape of an Image considering its rotation
* #param image
* #param x the x position of the Image
* #param y the y position of the Image
*/
public static Shape shapeFromImage(Image image, float x, float y) {
// create a rectangle with same position and size of the image
Shape imageShape = new Rectangle(x, y, image.getWidth(), image.getHeight());
// get the rotation angle of the image
float angle = image.getRotation();
// if the image is rotated, we also need to rotate our shape
if (angle != 0.f) {
// convert the rotation angle in radians to use in Transform
float angleInRadians = (float) Math.toRadians(angle);
// get the point of rotation to use in Transform.
// image.getCenterOfRotation returns a point relative to the image.
// for Transform we need an absolute point, so we add the image position to it
float rotationX = image.getCenterOfRotationX() + x;
float rotationY = image.getCenterOfRotationY() + y;
// create the rotation Transform to match the image rotation
Transform rotationTransform = Transform.createRotateTransform(angleInRadians, rotationX, rotationY);
// apply the rotation Transform to our shape
imageShape = imageShape.transform(rotationTransform);
}
return imageShape;
}
In your example you could use it like this:
float positionX;
float positionY;
if (angle == 90) {
highlightEdge.setCenterOfRotation(highlightEdge.getWidth(), 0);
highlightEdge.rotate(new Float(angle));
positionX = testPoint.x - 56;
positionY = testPoint.y;
highlightEdge.draw(positionX, positionY);
}
...
// you can now use this Shape to use its method "contains"
imageShape = shapeFromImage(highlightEdge, positionX, positionY);
Follow-up for: Calculating world coordinates from camera coordinates
I'm multiplying a 2D vector with a transformation matrix (OpenGL's model-view matrix) to get world coordinates from my camera coordinates.
I do this calculation like this:
private Vector2f toWorldCoordinates(Vector2f position) {
glPushMatrix();
glScalef(this.zoom, this.zoom, 1);
glTranslatef(this.position.x, this.position.y, 0);
glRotatef(ROTATION, 0, 0, 1);
ByteBuffer m = ByteBuffer.allocateDirect(64);
m.order(ByteOrder.nativeOrder());
glGetFloatv(GL_MODELVIEW_MATRIX, m);
float x = (position.x * m.getFloat(0)) + (position.y * m.getFloat(4)) + m.getFloat(12);
float y = (position.x * m.getFloat(16)) + (position.y * m.getFloat(20)) + m.getFloat(28);
glPopMatrix();
return new Vector2f(x, y);
}
Now I also want to do this vice-versa: calculate the camera coordinates for a position in the world. How can I reverse this calculation?
To create a matrix representing the inverse transform to the one above, apply the transforms in reverse, with negative quantities for the rotation and translation and an inverse quantity for the zoom:
glRotatef(-ROTATION, 0, 0, 1);
glTranslatef(-this.position.x, -this.position.y, 0);
glScalef(1.0f / this.zoom, 1.0f / this.zoom, 1);
Then multiply by the position vector as before.
The alternative is to compute the inverse matrix, but this way is much simpler.
I am attempting to create a 3D game using solely LWJGL and Slick-util, and I intend for it to be a first-person game, so I need a way to navigate the map using 3D movement. I have a position Vector3f and another vector acc to store acceleration. I have set up the following methods, all bound (in another class) to W, A, S, and D:
public void walkForward()
{
acc.x += walkSpeed * (float) Math.sin(Math.toRadians(yaw)); // Calculations for 3D Movement (please correct me if wrong)
acc.z -= walkSpeed * (float) Math.cos(Math.toRadians(yaw));
}
public void walkBackward()
{
acc.x -= walkSpeed * (float) Math.sin(Math.toRadians(yaw));
acc.z += walkSpeed * (float) Math.cos(Math.toRadians(yaw));
}
public void strafeLeft()
{
acc.x += walkSpeed * (float) Math.sin(Math.toRadians(yaw - 90));
acc.z -= walkSpeed * (float) Math.cos(Math.toRadians(yaw - 90));
}
public void strafeRight()
{
acc.x += walkSpeed * (float) Math.sin(Math.toRadians(yaw + 90));
acc.z -= walkSpeed * (float) Math.cos(Math.toRadians(yaw + 90));
}
Where walkSpeed is a positive float. Also, in my move() method, where movement is actually processed, I have the following:
void move()
{
acc.y = 0.0f; // Keep the player's height stable, for testing purposes.
getPosition().x += acc.x; // Add current velocity to the position.
getPosition().y += acc.y;
getPosition().z += acc.z;
if(acc.x > 0.0f) // Gradually bring player's velocity to 0.
acc.x -= 0.01f;
else if(acc.x < 0.0f)
acc.x += 0.01f;
if(acc.y > 0.0f)
acc.y -= 0.01f;
else if(acc.y < 0.0f)
acc.y += 0.01f;
if(acc.z > 0.0f)
acc.z -= 0.01f;
else if(acc.z < 0.0f)
acc.z += 0.01f;
}
Finally, in the render() method, where transformations are actually made to the game, I have this:
public void render()
{
move();
glRotatef(pitch, 1.0f, 0.0f, 0.0f);
glRotatef(yaw, 0.0f, 1.0f, 0.0f);
glTranslatef(-position.x, -position.y, -position.z);
}
Expected result: Ordinary 3D movement, proper directional motion, etc.
Actual result: Player moves in general direction, speed changes based on both yaw and pitch, releasing all keys (debugging confirms that NO input is being received to the walk() methods causes jittering and strange movement in random directions, whose speed changes based on both yaw and pitch, holding both W + A or W + D causes a massive increase in horizontal speed, etc.
I have a feeling this could be due to missing a pop or push in the matrix, or forgetting to init the identity somewhere. Any help would be appreciated. Thanks in advance!
Methods that manipulate the legacy OpenGL matrix stack, like glRotatef() and glTranslatef(), concatenate the specified transformation with the transformation that is currently on the matrix stack. Since you never reset/restore the transformation, you just keep concatenating more transformations every time render() is called.
To avoid this, you can either reset the transformation before starting to apply the current transformation:
glLoadIdentity();
glRotatef(pitch, 1.0f, 0.0f, 0.0f);
glRotatef(yaw, 0.0f, 1.0f, 0.0f);
glTranslatef(-position.x, -position.y, -position.z);
or save the previous matrix at the start, and restore it at the end:
glPushMatrix();
glRotatef(pitch, 1.0f, 0.0f, 0.0f);
glRotatef(yaw, 0.0f, 1.0f, 0.0f);
glTranslatef(-position.x, -position.y, -position.z);
glPopMatrix();
The second approach has the advantage that it will work correctly if you already had a matrix on the stack (e.g. a view transformation) that you also want to apply, while the first one resets all previous transformations.
Also note that transformations are applied to vertices in the reverse order of being specified. So your transformation sequence will first translate the vertices, then apply the yaw rotation, then the pitch rotation. If that's what you want, it's all good. Otherwise, you'll need to reverse the order.
Alright, I'm trying to do some simple object moving in the direction of where you touched the screen.
If I touch directly northwest of the object, it'll kind of move into the direction of the touch position. If I touch directly southeast of the object, it will kind of move into the direction of the touch position as well. However, if I touch directly northeast of the object, it'll move into the opposite direction towards the southwest. If I touch directly southwest of the object, it'll also move to the opposite direction towards northeast.
Also, if I touch north of the object, but just a little to the west, it will go straight west with a little to the north. Same with touching west of the object with a little bit to the north, it'll go straight north with a little bit to the west. Same thing for other directions.
Really, all the directions are from somewhat to obviously incorrect. I've been doing some paper calculations as well and I've seemed to be getting some correct angles, but at this point I'm completely stumped.
Does anyone know what the problem may be?
package com.badlogic.androidgames.texasholdem;
import java.util.List;
import android.util.FloatMath;
import com.badlogic.androidgames.framework.Game;
import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Input.TouchEvent;
import com.badlogic.androidgames.framework.Screen;
public class MainMenuScreen extends Screen {
public static float TO_RADIANS = (1 / 180.0f) * (float) Math.PI;
public static float TO_DEGREES = (1 / (float) Math.PI) * 180;
float num_x = 0; // Position of object on X axis
float num_y = 0; // Position of object on Y axis
float angle = 0;
public MainMenuScreen(Game game) {
super(game);
}
public void update(float deltaTime) {
Graphics g = game.getGraphics();
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
game.getInput().getKeyEvents();
int len = touchEvents.size();
for(int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if(event.type == TouchEvent.TOUCH_UP) {
if(inBounds(event, 0, 0, g.getWidth(), g.getHeight()) ) {
// Calculate the angle of the direction between two points
angle = (float) Math.atan2(event.x - num_x, event.y - num_y) * TO_DEGREES;
if (angle < 0)
angle += 360;
// This is just to give me numbers on the Math.atan2 result, angle, to/from X position, and to/from Y position
System.out.println("Pressed! - ATAN: " + Math.atan2(event.x - num_x, event.y - num_y)
+ " - ANGLE:" + angle + " - POS: " + event.x + "tx/"
+ (int)num_x + "fx " + event.y + "ty/" + (int)num_y + "fy");
}
}
}
// Moving object in direction at 1f speed
num_x += (1f * (float) Math.cos(angle * TO_RADIANS));
num_y += (1f * (float) Math.sin(angle * TO_RADIANS));
}
private boolean inBounds(TouchEvent event, int x, int y, int width, int height) {
if(event.x > x && event.x < x + width - 1 &&
event.y > y && event.y < y + height - 1)
return true;
else
return false;
}
public void present(float deltaTime) {
Graphics g = game.getGraphics();
g.drawPixmap(Assets.background, 0, 0);
g.drawPixmap(Assets.backcard, (int)num_x, (int)num_y);
}
public void pause() {
Settings.save(game.getFileIO());
}
public void resume() {
}
public void dispose() {
}
}
if event x> x then x must be positive to move toward event.x
the problem here is that when event.x< x then your moving x must be negative
int dx,dy;
dx = (1f * (float) Math.cos(angle * TO_RADIANS));
dy = (1f * (float) Math.sin(angle * TO_RADIANS));
if(event.x<x){
dx=-dx;}
if(event.y<y){
dy=-dy;}
num_x+=dx;
num_y+=dy;
this way is simpler but less precise....
public void update(){
//(find dif between item x, and touch x)
float xdif=destx-x;
float ydif=desty-y;
if(x<destx){
dx=xdif/8;
}
else if(x>destx){
//we devide both x and y differences by the same number
dx=xdif/8;
}
else if(x==destx){
dx=0;
}
if(y<desty){
dy=ydif/5;
}
else if(y>desty){
dy=ydif/5;
}
else if(y==desty){
dy=0;
}
x+=dx;
y+=dy;
there u go, pathing in a straight line between two points, item.x and touch x.
Firstly, the math - I think the problem is that, for example, tan(135deg) = tan (-45deg) = -1. Therefore, atan has return values ranging between -90deg and 90deg as a resolution to ambiguity (look at its graph here). I think La5t5tarfighter's solution - negating the x movement in some cases - is on the right track, but you need to negate the y component in those cases as well. You could try that, but it would be much simpler if you used libGDX's Vector2 class. This is how I'd do it:
move.set(touchX, touchY); // y should be through flipping or unproject() before this
move.sub(objectPos); // move now points from object to where you touched
move.nor(); // now 1 unit long
move.scl(SPEED*deltaTime); // multiplied by a constant and delta - framerate-independent
objectPos.add(move);
You could even chain it into just one line if you want:
objectPos.add(move.set(x,y).sub(objectPos).nor().scl(SPEED*deltaTime));
Secondly, you're not using a Camera. I'm not completely sure what the default coordinate system is, but I believe the y axis points up which is not the same as the one used for inputs - Input.getY() is given with an y axis pointing down from the top left corner. If you had a Camera, you'd do this:
cam.unproject(someVector.set(Gdx.input.getX(), Gdx.input.getY(), 0));
Lacking that, you might need to flip the y axis:
event.y = Gdx.graphics.getHeight() - event.y;
Still, this could be wrong. Try drawing the object right at the touch position - if I'm right in this, it'll seem mirrored vertically. If it draws correctly where you touch, ignore this part.
I'm trying to rotate and move a triangle into a certain direction, based on the pointing direction of the triangle. In theory, I calculate the sine and cosine of the direction (0-360 degrees) and add these values to the x- and y-position, right? It just doesn't work.
Also, the triangle should point up in the beginning, not down.
public void speedUp() {
float dirX, dirY;
speed *= acceleration;
if(speed > 50) speed = 50;
println("dir: " + direction + " x: " + cos(direction) + " y: " + sin(direction));
dirX = cos(direction);
dirY = sin(direction);
xPos+=dirX;
yPos+=dirY;
}
public void redraw() {
GL gl = pgl.beginGL(); // always use the GL object returned by beginGL
gl.glTranslatef(xPos, yPos, 0);
gl.glRotatef(direction, 0, 0, 1000);
gl.glBegin(GL.GL_TRIANGLES);
gl.glColor4f(0.1, 0.9, 0.7, 0.8);
gl.glVertex3f(-10, -10, 0); // lower left vertex
gl.glVertex3f( 10, -10, 0); // lower right vertex
gl.glVertex3f( 0, 15, 0); // upper vertex
gl.glEnd();
}
It looks like you need to convert from polar coordinates(moving about using an angle and a radius) to cartesian coordinates(moving about using the x and y).
The formula looks a bit like this:
x = cos(angle) * radius;
y = sin(angle) * radius;
So, as #Lie Ryan mentions, you also need to multiply with speed (which is your radius in polar coordinates).
Either have your angle in degrees but use radians() when using cos,sin as they work with radians, or use radians, and use degrees() with glRotatef, up to you
Also, you might want to have a look at glPushMatrix() and glPopMatrix(). Bascially, they allow you to nest transformations. Whatever transformations you do withing the blocks, they affect just that block locally.
Here's what I mean, use w,a,s,d keys:
import processing.opengl.*;
import javax.media.opengl.*;
float direction = 0;//angle in degrees
float speed = 0;//radius
float xPos,yPos;
void setup() {
size(600, 500, OPENGL);
}
void keyPressed(){
if(key == 'w') speed += 1;
if(key == 'a') direction -= 10;
if(key == 'd') direction += 10;
if(key == 's') speed -= 1;
if(speed > 10) speed = 10;
if(speed < 0) speed = 0;
println("direction: " + direction + " speed: " + speed);
}
void draw() {
//update
xPos += cos(radians(direction)) * speed;
yPos += sin(radians(direction)) * speed;
//draw
background(255);
PGraphicsOpenGL pgl = (PGraphicsOpenGL) g;
GL gl = pgl.beginGL();
gl.glTranslatef(width * .5,height * .5,0);//start from center, not top left
gl.glPushMatrix();
{//enter local/relative
gl.glTranslatef(xPos,yPos,0);
gl.glRotatef(direction-90,0,0,1);
gl.glColor3f(.75, 0, 0);
gl.glBegin(GL.GL_TRIANGLES);
gl.glVertex2i(0, 10);
gl.glVertex2i(-10, -10);
gl.glVertex2i(10, -10);
gl.glEnd();
}//exit local, back to global/absolute coords
gl.glPopMatrix();
pgl.endGL();
}
You don't actually need the { } for the push and pop matrix calls, I added them like a visual aid. Also, you can do this without push/pop, by concatenating your transforms, but it's handy to know those are there for your when you need them. Might come in handy when you want to shoot some GL_LINES out of that triangle...pew pew pew!
HTH
You have your units messed up. glRotatef excepts degrees and the trigonometrical functions expect radians. This is the most obvious mistake.
Also, speed is never used in your snippet. I suppose that every frame you're using it somehow, but in the code you pasted there's:
xPos+=dirX
Which is basically "add direction to the position" - not making much sense, unless you want to "move it exactly 1 unit in the given direction instantenously at the moment when speedUp() is called. The usual approach for continous movement would be to:
// each frame:
xPos += dirX * speed * deltaTime;
yPos += dirY * speed * deltaTime;
Try this:
dirX = speed * cos(direction);
dirY = speed * sin(direction);
You are obviously new to OpenGl, so I would recommend you, that you look into quaternions to do your roations. Here are two pretty nice article about this matter: Gamedev and Nehe. I would recommend you to use the Quaternion class from the JMonkeyEngine. Just remove the savable and some other interfaces and you can use them with ease. Here they are located: JMonkey Source
I also use the JME math classes for my own projects. I have already striped most of the dependencies and you can download some classes from here: Volume Shadow. However the Quaternion class is missing, but you WILL need Vector3f :D.