I have the following Java class I've written for a LibGdx OpenGL project.
The camera keeps the aspect ratio of the screen no matter how you resize it by letterboxing either the top and bottom, or the sides. So far, so good.
The issue comes when I try to obtain the mouse x, y coordinates of a click, and the letterbox is involved for that axis.
First here is the class:
public class Camera {
private static float viewportWidth;
private static float viewportHeight;
private static float aspectRatio;
private static float barSize;
/**
* Creates an orthographic camera where the "play area" has the given viewport size. The viewport will be scaled to maintain the aspect ratio.
*
* #param virtualWidth the width of the game screen in virtual pixels.
* #param virtualHeight the height of the game screen in virtual pixels.
* #return the new camera.
*
*/
public static OrthographicCamera createCamera(float virtualWidth, float virtualHeight) {
aspectRatio = virtualWidth / virtualHeight;
float physicalWidth = Gdx.graphics.getWidth();
float physicalHeight = Gdx.graphics.getHeight();
if (physicalWidth / physicalHeight >= aspectRatio) {
// Letterbox left and right.
viewportHeight = virtualHeight;
viewportWidth = viewportHeight * physicalWidth / physicalHeight;
barSize = ????;
}
else {
// Letterbox above and below.
viewportWidth = virtualWidth;
viewportHeight = viewportWidth * physicalHeight / physicalWidth;
barSize = ????;
}
OrthographicCamera cam = new OrthographicCamera(viewportWidth , viewportHeight);
cam.position.set(virtualWidth / 2, virtualHeight / 2, 0);
cam.rotate(180, 1, 0, 0);
cam.update();
Gdx.app.log("BTLog", "barSize:"+barSize);
return cam;
}
public static float getViewportWidth() {
return viewportWidth;
}
public static float getViewportHeight() {
return viewportHeight;
}
}
LibGdx supplies me the x and y coordinates when an even happens, and I need to translate these raw coordinates into the scale of my camera (the virtual height and width).
When the screen is stretched (no letterboxing at all), it's pretty easy to obtain the x and y coordinates by using:
xRelative = (int) (x / (float)Gdx.graphics.getWidth() * Camera.getViewportWidth());
yRelative = (int) (y / (float)Gdx.graphics.getHeight() * Camera.getViewportHeight());
The problem is when the letterboxes come into play, it throws off the coordinate for that axis. I know I need to take into account the width of the letterboxing, but i'm having a hell of a time figuring how to calculate it.
Above where I have "barSize = ????;" my first instinct was to do this:
barSize = physicalHeight - viewportHeight; // to use height for example
Once I get the barSize, i'm fairly sure I can use this to get the right numbers (using the y axis for example):
yRelative = (int) (y / (float)Gdx.graphics.getHeight() * Camera.getViewportHeight() - Camera.getBarSize());
But the numbers don't match up. Any suggestions would be a really appreciated!
Ray ray = camera.getPickRay(x, y);
System.out.println(ray.origin.x);
System.out.println(ray.origin.y);
Related
I am trying to touch and drag the card and update its position. Firstly, I want the card class to detect the touch drag event when I click on the card.
I have provided the code below for the card class and want help on how to detect touch event. I want to know what methods I should add and how to implement the x and y coordinates.
public class Card extends Sprite {
// /////////////////////////////////////////////////////////////////////////
// Properties:
// /////////////////////////////////////////////////////////////////////////
// Define the default card width and height
private static final int DEFAULT_CARD_WIDTH = 180;
private static final int DEFAULT_CARD_HEIGHT = 260;
// Define the common card base
private Bitmap mCardBase;
// Define the card portrait image
private Bitmap mCardPortrait;
// Define the card digit images
private Bitmap[] mCardDigits = new Bitmap[10];
// Define the offset locations and scaling for the card portrait
// card attack and card health values - all measured relative
// to the centre of the object as a percentage of object size
private Vector2 mAttackOffset = new Vector2(-0.68f, -0.84f);
private Vector2 mAttackScale = new Vector2(0.1f, 0.1f);
private Vector2 mHealthOffset = new Vector2(0.72f, -0.84f);
private Vector2 mHealthScale = new Vector2(0.1f, 0.1f);
private Vector2 mPortraitOffset = new Vector2(0.0f, 0.3f);
private Vector2 mPortraitScale = new Vector2(0.55f, 0.55f);
// Define the health and attack values
private int mAttack;
private int mHealth;
// /////////////////////////////////////////////////////////////////////////
// Constructors
// /////////////////////////////////////////////////////////////////////////
/**
* Create a new platform.
*
* #param x Centre y location of the platform
* #param y Centre x location of the platform
* #param gameScreen Gamescreen to which this platform belongs
*/
public Card(float x, float y, GameScreen gameScreen) {
super(x, y, DEFAULT_CARD_WIDTH, DEFAULT_CARD_HEIGHT, null, gameScreen);
AssetManager assetManager = gameScreen.getGame().getAssetManager();
// Store the common card base image
mCardBase = assetManager.getBitmap("CardBackground");
// Store the card portrait image
mCardPortrait = assetManager.getBitmap("CardPortrait");
// Store each of the damage/health digits
for(int digit = 0; digit <= 9; digit++)
mCardDigits[digit] = assetManager.getBitmap(String.valueOf(digit));
// Set default attack and health values
mAttack = 1;
mHealth = 2;
}
// /////////////////////////////////////////////////////////////////////////
// Methods
// /////////////////////////////////////////////////////////////////////////
/**
* Draw the game platform
*
* #param elapsedTime Elapsed time information
* #param graphics2D Graphics instance
* #param layerViewport Game layer viewport
* #param screenViewport Screen viewport
*/
#Override
public void draw(ElapsedTime elapsedTime, IGraphics2D graphics2D,
LayerViewport layerViewport, ScreenViewport screenViewport) {
// Draw the portrait
drawBitmap(mCardPortrait, mPortraitOffset, mPortraitScale,
graphics2D, layerViewport, screenViewport);
// Draw the card base background
mBitmap = mCardBase;
super.draw(elapsedTime, graphics2D, layerViewport, screenViewport);
// Draw the attack value
drawBitmap(mCardDigits[mAttack], mAttackOffset, mAttackScale,
graphics2D, layerViewport, screenViewport);
// Draw the attack value
drawBitmap(mCardDigits[mHealth], mHealthOffset, mHealthScale,
graphics2D, layerViewport, screenViewport);
}
private BoundingBox bound = new BoundingBox();
/**
* Method to draw out a specified bitmap using a specific offset (relative to the
* position of this game object) and scaling (relative to the size of this game
* object).
*
* #param bitmap Bitmap to draw
* #param offset Offset vector
* #param scale Scaling vector
* #param graphics2D Graphics instance
* #param layerViewport Game layer viewport
* #param screenViewport Screen viewport
*/
private void drawBitmap(Bitmap bitmap, Vector2 offset, Vector2 scale,
IGraphics2D graphics2D, LayerViewport layerViewport, ScreenViewport screenViewport) {
// // Calculate a game layer bound for the bitmap to be drawn
// bound.set(position.x + mBound.halfWidth * offset.x,
// position.y + mBound.halfHeight * offset.y,
// mBound.halfWidth * scale.x,
// mBound.halfHeight * scale.y);
// Calculate the center position of the rotated offset point.
double rotation = Math.toRadians(-this.orientation);
float diffX = mBound.halfWidth * offset.x;
float diffY = mBound.halfHeight * offset.y;
float rotatedX = (float)(Math.cos(rotation) * diffX - Math.sin(rotation) * diffY + position.x);
float rotatedY = (float)(Math.sin(rotation) * diffX + Math.cos(rotation) * diffY + position.y);
// Calculate a game layer bound for the bitmap to be drawn
bound.set(rotatedX, rotatedY,
mBound.halfWidth * scale.x, mBound.halfHeight * scale.y);
// Draw out the specified bitmap using the calculated bound.
// The following code is based on the Sprite's draw method.
if (GraphicsHelper.getSourceAndScreenRect(
bound, bitmap, layerViewport, screenViewport, drawSourceRect, drawScreenRect)) {
// Build an appropriate transformation matrix
drawMatrix.reset();
float scaleX = (float) drawScreenRect.width() / (float) drawSourceRect.width();
float scaleY = (float) drawScreenRect.height() / (float) drawSourceRect.height();
drawMatrix.postScale(scaleX, scaleY);
drawMatrix.postRotate(orientation, scaleX * bitmap.getWidth()
/ 2.0f, scaleY * bitmap.getHeight() / 2.0f);
drawMatrix.postTranslate(drawScreenRect.left, drawScreenRect.top);
// Draw the bitmap
graphics2D.drawBitmap(bitmap, drawMatrix, null);
}
}
}
I have an image in Graphics2D that I need to rotate and then obtain the new co-ordinates of the image corners and the dimensions of the new bounding box.
I was originally trying to work with the image itself but I think it would be easier to work with a rectangle (or polygon) to give myself more flexibility. I was originally performing the rotation on the image simply with AffineTransform.rotate(). However, it would be cleaner if there was a way to translate each corner point individually, that would give me the values of A1, B1, C1 & D1. Is there a way in Graphics2D to rotate the individual corners?
I have found several questions relating to the bounding box dimensions of a rotated rectangle but I can't seem to get any of them to work in Java with Graphics2D.
You'll simply have to rotate the image corners yourself. The package java.awt.geom provides the classes Point2D and AffineTransform to do that by applying a rotation transform to individual points. The width and height of the rotated bounding box can be computed as the difference between the maximum and maximum rotated x and y coordinates, with the minimum x and y coordinate as offset.
The following program implements this algorithm and displays the results for several rotations from 0° to 360° in 30° steps:
package stackoverflow;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
/**
* Demonstration of an implementation to rotate rectangles.
* #author Franz D.
*/
public class ImageRotate
{
/**
* Rotates a rectangle with offset (0,0).
* #param originalWidth original rectangle width
* #param originalHeight original rectangle height
* #param angleRadians rotation angle in radians
* #param rotatedCorners output buffer for the four rotated corners
* #return the bounding box of the rotated rectangle
* #throws NullPointerException if {#code rotatedCorners == null}.
* #throws ArrayIndexOutOfBoundsException if {#code rotatedCorners.length < 4}.
*/
public static Rectangle2D rotateRectangle(int originalWidth, int originalHeight,
double angleRadians,
Point2D[] rotatedCorners) {
// create original corner points
Point2D a0 = new Point2D.Double(0, 0);
Point2D b0 = new Point2D.Double(originalWidth, 0);
Point2D c0 = new Point2D.Double(0, originalHeight);
Point2D d0 = new Point2D.Double(originalWidth, originalHeight);
Point2D[] originalCorners = { a0, b0, c0, d0 };
// create affine rotation transform
AffineTransform transform = AffineTransform.getRotateInstance(angleRadians);
// transform original corners to rotated corners
transform.transform(originalCorners, 0, rotatedCorners, 0, originalCorners.length);
// determine rotated width and height as difference between maximum and
// minimum rotated coordinates
double minRotatedX = Double.POSITIVE_INFINITY;
double maxRotatedX = Double.NEGATIVE_INFINITY;
double minRotatedY = Double.POSITIVE_INFINITY;
double maxRotatedY = Double.NEGATIVE_INFINITY;
for (Point2D rotatedCorner: rotatedCorners) {
minRotatedX = Math.min(minRotatedX, rotatedCorner.getX());
maxRotatedX = Math.max(maxRotatedX, rotatedCorner.getX());
minRotatedY = Math.min(minRotatedY, rotatedCorner.getY());
maxRotatedY = Math.max(maxRotatedY, rotatedCorner.getY());
}
// the bounding box is the rectangle with minimum rotated X and Y as offset
double rotatedWidth = maxRotatedX - minRotatedX;
double rotatedHeight = maxRotatedY - minRotatedY;
Rectangle2D rotatedBounds = new Rectangle2D.Double(
minRotatedX, minRotatedY,
rotatedWidth, rotatedHeight);
return rotatedBounds;
}
/**
* Simple test for {#link #rotateRectangle(int, int, double, java.awt.geom.Point2D[])}.
* #param args ignored
*/
public static void main(String[] args) {
// setup original width
int originalWidth = 500;
int originalHeight = 400;
// create buffer for rotated corners
Point2D[] rotatedCorners = new Point2D[4];
// rotate rectangle from 0° to 360° in 30° steps
for (int angleDegrees = 0; angleDegrees < 360; angleDegrees += 30) {
// convert angle to radians
double angleRadians = Math.toRadians(angleDegrees);
// rotate rectangle
Rectangle2D rotatedBounds = rotateRectangle(
originalWidth, originalHeight,
angleRadians,
rotatedCorners);
// dump results
System.out.println("--- Rotate " + originalWidth + "x" + originalHeight + " by " + angleDegrees + "° ---");
System.out.println("Bounds: " + rotatedBounds);
for (Point2D rotatedCorner: rotatedCorners) {
System.out.println("Corner " + rotatedCorner);
}
}
}
}
If your image is not placed at offset (0, 0), you can simply modify the method to have the offset as input parameter, and adding the offset coordinates to the original points.
Also, this method rotates the image (or rectangle) about the origin (0, 0). If you want other rotation centers, AffineTransform provides an overloaded variant of getRotateInstace() which allows you to specify the rotation center (called "anchor" in the API documentation).
I am drawing some 2Dgraphics to JPanel.
I don't understant how to use scale if i change JPanelsize?
I have an image, which is composed of multiple lines and circles.
Is possible to compute a transformation and that multiply the values that miscalculation? How? Can you give me an example?
A neat way to do it (but not the best) is to have a final variable for the Width and Height, and everything is calculated based on those variables. then you could have a scale variable that is the size of the (JPanel / final Variable) and when you do g.draw() you can set the width to width * wScale, and the height to height * hScale. here is an example
public final WIDTH = 1080;
public final HEIGHT = WIDTH * 16 / 9;
private float wScale = 1;
private float hScale = 1;
public static void mainLoop()
{
wScale = jpanelWidth / (WIDTH * 1.0);
hScale = jpanelHeight / (HEIGHT * 1.0);
//makes the image to draw using the final width and height for calculations
someclass.makeImage(WIDTH, HEIGHT);
//gets an image that has the dimentions of WIDTH and HEIGHT
someimage = someclass.getImage();
//draws the image using its width and scales it to the actual size by multiplying it by the scaler
//the two zeros are at position (0, 0), and null is just the observer.
somegraphics.draw(someimage, 0, 0, (WIDTH * wScale), (HEIGHT * hScale), null);
}
if you need more information or have questions, you can ask for clarification, or contact me personally.
I am new to LWJGL but am slowly learning. I was wanting to make a square that rotated when you pressed the key. Like d rotates it 90 degrees as you can tell below, but when I use glRotatef(); it gives me an error and I don't know why. There error tells me I need to create a method for it, I know I don't need to though. Anything helps!
public class MainPlayer {
private Draw draw;
private int rotation;
private float WIDTH = (float) (Display.getWidth() * 0.1);
private float HEIGHT = (float) (WIDTH / 2);
private float x = Display.getWidth() / 2 - WIDTH / 2;
private float y = Display.getHeight() / 2 - HEIGHT / 2;
public MainPlayer(){
draw = new Draw(1.0f, 1.0f, 1.0f, WIDTH, HEIGHT);
}
public void update(){
}
public void render(){
glTranslatef(x, y, 0);
glRotatef(rotation,0,0,1);
draw.render();
}
public void getInput(){
if(Keyboard.isKeyDown(Keyboard.KEY_W)){
rotation = 0;
}
if(Keyboard.isKeyDown(Keyboard.KEY_S)){
rotation = 180;
}
if(Keyboard.isKeyDown(Keyboard.KEY_A)){
rotation = 270;
}
if(Keyboard.isKeyDown(Keyboard.KEY_D)){
rotation = 90;
}
}
}
You create an int rotation and I assume your render() loops the whole time, and you only set rotation in getInput().
So I am assuming that you should declare it as int rotation = 0.
glRotatef() is a OpenGL call to rotate objects, just like glTranslatef() moves them. glRotatef() does it in the same way.
glRotatef(AngleOfRotationf, 0, 1, 0) would rotate it horisontally, like in this video i just made: http://www.youtube.com/watch?v=SHsssrj9qr8& uses that line to rotate a ship.
Also in that video i demonstrated moving it with glTranslatef().
To use it you must use GL11.glRotatef(), or import static org.lwjgl.opengl.GL11.*;
That probably means that you haven't statically imported glRotatef
Either use
GL11.glRotatef(rotation, 0, 0, 1);
or import it at the beginning of your program with
import static org.lwjgl.opengl.GL11.glRotatef
i am looking for an animation for a view flipper.
How would i go about animating so that the view basically goes back to front when a button is pressed. where there is a map view on one side and a listview on the other.
i have the above working except for the animation . could someone help or explain android animations to me
edit added here
here is some code for animation for rotation, how could i implment this in xml
public class Rotate3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
/**
* Creates a new 3D rotation on the Y axis. The rotation is defined by its
* start angle and its end angle. Both angles are in degrees. The rotation
* is performed around a center point on the 2D space, definied by a pair
* of X and Y coordinates, called centerX and centerY. When the animation
* starts, a translation on the Z axis (depth) is performed. The length
* of the translation can be specified, as well as whether the translation
* should be reversed in time.
*
* #param fromDegrees the start angle of the 3D rotation
* #param toDegrees the end angle of the 3D rotation
* #param centerX the X center of the 3D rotation
* #param centerY the Y center of the 3D rotation
* #param reverse true if the translation should be reversed, false otherwise
*/
public Rotate3dAnimation(float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
It depends on what you want to do but you can lookup wiEngine for Android SDK, at the transitions example... take a look at the code thought the documentation is in chinese, you can simply read the code.
Also take a look at Chet Haase blog for some tips, http://graphics-geek.blogspot.com/ he has a bunch of stuff related to animation. Targeting API level12 that would take just about one line of code using the method objectToAnimate.rotateYBy(360).setDuration(250f)