Android Screen coordinates to canvas view coordinates - java

I am trying to turn my screen x and y coordinates to the ones that's used to draw on screen.
So I get my screen X and Y coordinates from MotionEvent thats fired by my touch listener.
I thought it should be as easy as multiplying those by the matrix that's used to draw on canvas so I creatad Matrix instance at creation of view
matrix = new Matrix();
when void onDraw(Canvas canvas) gets called, I set the canvas' matrix to be the matrix I created on the constructor and apply all my transformations to the matrix
matrix.reset();
canvas.setMatrix(matrix);
canvas.translate(getWidth() / 2, getHeight() / 2);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.translate(-getWidth() / 2, -getHeight() / 2);
canvas.translate(-x, -y);
The view looks as it should but when on my touch listener I try to turn my screen coordinates to view coordinates with that matrix using mapPoints(float[] points) the values it gives aren't right, I draw cross on 0, 0 in onDraw
canvas.drawLine(0f, -100f, 0f, 100f, viewportPaint);
canvas.drawLine(-100f, 0f, 100, 0f, viewportPaint);
and when I click where it appears to be after scaling and what not the values I receive aren't even close to 0, 0
float[] array = new float[]{e.getX(), e.getY()};
matrix.mapPoints(array);
Log.v(TAG, "points transformed are " + array[0] + ", " + array[1]);
When I took this picture where I am clearly clicking the 0, 0 mark I received the following logging:
03-09 14:08:48.803: V/XXXX(22181): points transformed are 403.43967, 628.47
Ps. I am not touching matrix anywhere else than in my onDraw code

I had the question in my mind inverted, when I thought about the question again after having a break from it(after 8 hours..) I got it, I had to invert the matrix in order to perform this operation.
Also the matrix wasn't transformed when I was transforming canvas so i had to canvas.get(matrix)
in order to get the same matrix that the canvas was using..

This might help others:
In onDraw()
canvasMatrix.postScale(mScaleFactor, mScaleFactor);
canvasMatrix.postTranslate(-getWidth() / 2, -getHeight() / 2);
canvas.setMatrix(canvasMatrix);
Somewhere else where you need the mapped coordinates:
Matrix m = new Matrix();
canvasMatrix.invert(m);
float[] touch = new float[] { X, Y };
m.mapPoints(touch);
X = touch[0];
Y = touch[1];
there you go.

Related

Scale matrix in place

currently I am scaling a matrix like so:
public void scale(float aw, float ah){
Matrix.scaleM(modelMatrix, 0, aw, ah, 1f);
updateMVP();
}
private void updateMVP(){
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
}
And using: gl_Position = u_Matrix * a_Position; in my vertex shader, u_Matrix being the mvpMatrix. The camera I am using is the default and the projectionMatrix is created by:
ASPECT_RATIO = (float) height / (float) width;
orthoM(projectionMatrix, 0, -1f, 1f, -ASPECT_RATIO, ASPECT_RATIO, -1f, 1f);
Now I can scale my object properly, but the only problem is that every time I scale the matrix, the object moves a little bit. I was wondering how I could scale the matrix while keeping the center point and not having the object translate. Anyone know how I can do this in OpenGL ES 2.0 on Android? Thanks
Do you have any other matrices (rotation/translation)?
If so: you might not be multiplying your matrices in the correct order, which can cause issues.
(proper order multiply right to left)
Translate * Rotation * Scale
Your error sounds like the one explained here:
You translate the ship by (10,0,0). Its center is now at 10 units of the origin.
You scale your ship by 2. Every coordinate is multiplied by 2 relative to the origin, which is far away… So you end up with a big
ship, but centered at 2*10 = 20. Which you don’t want.
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/

How to translate the camera in GLES2.0?

I want to create a camera moving above a tiled plane. The camera is supposed to move in the XY-plane only and to look straight down all the time. With an orthogonal projection I expect a pseudo-2D renderer.
My problem is, that I don't know how to translate the camera. After some research it seems to me, that there is nothing like a "camera" in OpenGL and I have to translate the whole world. Changing the eye-position and view center coordinates in the Matrix.setLookAtM-function just leads to distorted results.
Translating the whole MVP-Matrix does not work either.
I'm running out of ideas now; do I have to translate every single vertex every frame directly in the vertex buffer? That does not seem plausible to me.
I derived GLSurfaceView and implemented the following functions to setup and update the scene:
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// Setup the projection Matrix for an orthogonal view
Matrix.orthoM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//Setup the camera
float[] camPos = { 0.0f, 0.0f, -3.0f }; //no matter what else I put in here the camera seems to point
float[] lookAt = { 0.0f, 0.0f, 0.0f }; // to the coordinate center and distorts the square
// Set the camera position (View matrix)
Matrix.setLookAtM( vMatrix, 0, camPos[0], camPos[1], camPos[2], lookAt[0], lookAt[1], lookAt[2], 0f, 1f, 0f);
// Calculate the projection and view transformation
Matrix.multiplyMM( mMVPMatrix, 0, projMatrix, 0, vMatrix, 0);
//rotate the viewport
Matrix.setRotateM(mRotationMatrix, 0, getRotationAngle(), 0, 0, -1.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
//I also tried to translate the viewport here
// (and several other places), but I could not find any solution
//draw the plane (actually a simple square right now)
mPlane.draw(mMVPMatrix);
}
Changing the eye-position and view center coordinates in the "LookAt"-function just leads to distorted results.
If you got this from the android tutorial, I think they have a bug in their code. (made a comment about it here)
Try the following fixes:
Use setLookatM to point to where you want the camera to be.
In the shader, change the gl_Position line
from: " gl_Position = vPosition * uMVPMatrix;"
to: " gl_Position = uMVPMatrix * vPosition;"
I'd think the //rotate the viewport section should be removed as well, as this is not rotating the camera properly. You can change the camera's orientation in the setlookat function.

Rotate Image Clockwise using LibGDX

How can we rotate a Image Clockwise using LibGDX? what i am looking is when a image is loaded,suppose a star, i need to rotate it from beginning of screen to end of the screen horizontally, with star rotating,how can i do that in libgdx?
When you draw the Texture with your SpriteBatch, you can use one of the draw functions that includes rotation. This javadoc has all the draw functions: SpriteBatch
You can keep a variable for position and rotation, and increase the rotation and x component of the position each time you render to make it rotate while moving horizontally.
Libgdx gives you more then one way to do that:
You can use Scene2D and add an Image to your Stage. Image is a subclass of Actor, so you can add Actions to it:
Image myImage = new Image(myTexture);
myImage.addAction(Actions.parallel(Actions.moveTo(endX, endY, duration), Actions.rotateBy(degrees, duration)));
myImage.setPosition(startX, startY);
myImage.setOrigin(sizeX/2, sizeY/2);
stage.add(myImage);
In render you can then call stage.act(), which updates the position, rotation, scale... of all your Actors and then call stage.draw() which will call draw() for all your Actors.
Image allready handles the draw() so you don't need to care about that anymore.
You can also do it without scene2d, by updating everything yourself:
You can store a int rotationSpeed in degrees/sec
You can store a int moveSpeed in units/sec (maybe pixel but i would suggest to use camera or viewport and use your own unit, which is equal on all devices)
Store the float angle, which is the current rotation of your Texture
and store a Vector2 position, which contains the x and y position of your Texture.
If you want to move in x and y direction you can also store a Vector2 direction, which is a normalized Vector, giving the percent of movement in x and y direction, but thats a different story.
Then in your render(float delta) you update everything:
angle+=delta*rotationSpeed;
angl%=360; // Limits the angle to be <= 360
while (angle < 0) // Unfortunally the "modulo" in java gives negative result for negativ values.
angle+=360;
position.x+=direction.x*moveSpeed*delta;
position.y+=direction.y*movSpeed*delta;
spriteBatch.draw(yourTextureRegion, position.x, position.y, sizeX/2, sizeY/2, sizeX, sizeY, scaleX, scaleY, angle);
For clockwise rotation simply use a negative rotationSpeed or replace the angle+= with angle-=.
Hope it helps.
Following is the implementation to rotate any sprite
batch.draw(sprite,(Gdx.graphics.getWidth() - sprite.getRegionWidth()) / 2.0f,(Gdx.graphics.getHeight() - sprite.getRegionHeight()) / 2.0f,sprite.getRegionWidth()/2.0f,sprite.getRegionHeight()/2.0f, sprite.getRegionWidth(), sprite.getRegionHeight(), 1f, 1f,count, false);
if(count < 0.0f)
count = 360.0f;
else
count --;
Initially set counter to
private float count =360.0f;
You can also use the Scene2D actions. I have an example here with asteroid-type thing falling down the screen and rotating.
http://www.netthreads.co.uk/2012/02/09/libgdx-scene2d-demo-with-scene-transitions/
To rotate anticlockwise and horizontally..
create a textureRegion
then
Sprite sprite = new Sprite(textureRegion, 0, 0, 128, 128);
sprite.setPosition(++mX, 0);
angle++;
sprite.setRotation(angle);
sprite.draw(batcher);
You can do it too like this:
on your create method
sprite.setOrigin(sprite.getWitdh() /2f, sprite.getHeight() /2f);
sprite.setPosition( 0, 200 ); //200 it's a example
on your render(float delta)
sprite.setX( sprite.getX() + delta ).setRotation( sprite.getRotation() + delta );
Here is a simple to rotate an actor in libgdx. First you need to set the origin:
img.setOrigin(getWidth/2,getHeight/2);
And then you can rotate clockwise and anticlockwise according to your need:
img.rotate(2f); or img.rotate(-2f);
So the following sample worked for me (infinite rotation)
Method 1: (recommended)
loadingActor.addAction(Actions.repeat(RepeatAction.FOREVER, Actions.rotateBy(360, 1)));
Method 2:
Image loadingActor = new Image(AssetsController.getInstance().getLoading());
loadingActor.setOrigin(Align.center);
final SequenceAction infiniteRotate = Actions.sequence();
infiniteRotate.addAction(Actions.rotateTo(0 , 0f) );
infiniteRotate.addAction(Actions.rotateTo(360 , 1f) );
loadingActor.addAction(Actions.forever(infiniteRotate));

Android Bitmap/Canvas offset after scale

If I have a canvas, on which I draw a Bitmap like this:
canvas.drawBitmap(bmLargeImage, srcRect, destRect, paint);
and I scale the bitmap:
canvas.scale(1.5f, 1.5f, 450, 250);
I want to get the position of the Bitmap after the scale. If the position before scale was (0, 0), after scale there is a offset and I need that offset.. how can I get it?
Thanks and sorry for the simple question, newbie here...
Ok lets try to work out the best formula for this
canvas.scale(scaleX, scaleY, pivotX, pivotY);
if (scaleX >= 1){
objectNewX = objectOldX + (objectOldX - pivotX)*(scaleX - 1);
}else{
objectNewX = objectOldX - (objectOldX - pivotX)*(1 - scaleX);
}
The same for objectNewY. The new width and height of the bitmap would of course be the multiple of the old size and scale.
I believe the cleanest Solution would be to use the underlying transformation Matrix of the Canvas you are manipulating.
In Android there is the canvas.getMatrix(Matrix cmt) method available which will yield it. The transformation matrix will transform any point in world space you throw at into screen coordinates. Just use the matrix.mapPoints(float[] points) and you will be fine.
FYI, you can easily do it the other way around too. If you want to know what screen coordinate maps to which point in world space, e.g. for tapping; the inverse matrix can be used for that. It can be obtained via the matrix.invert(Matrix out) method. Use its mapPoints() for the coordinate mapping then.
Here are the official docs:
mapPoints(), invert(), getMatrix()
If you'd like know the corners of your screen relative to your original canvas, you can use canvas.getClipBounds(). This returns a Rect with edge coordinates relative to your original canvas. For instance, if you start off with a canvas size of 320 x 480 and call
canvas.scale(2, 2, getWidth()/2, getHeight()/2);
and then
canvas.getClipBounds();
you will have a Rect (call this rect) where
rect.top == 120
rect.bottom == 360
rect.left == 80
rect.right == 240

Matrix transformations in OpenGL

I'm building a 2D physics engine in Java using OpenGL (from LWJGL) to display the objects. The problem I am having is that the transformation matrices I apply to the frame seem to be getting applied in a different order to what it says that are.
/**
* Render the current frame
*/
private void render() {
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, Display.getDisplayMode().getWidth(), 0, Display
.getDisplayMode().getHeight(), -1, 1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
GL11.glPushMatrix();
GL11.glTranslatef(Display.getDisplayMode().getWidth() / 2, Display
.getDisplayMode().getHeight() / 2, 0.0f);
renderFrameObjects();
GL11.glPopMatrix();
}
public void renderFrameObjects() {
Vector<ReactiveObject> objects = frame.getObjects();
for (int i = 0; i < objects.size(); i++) {
ReactiveObject currentObject = objects.get(i);
Mesh2D mesh = currentObject.getMesh();
GL11.glRotatef((float)(currentObject.getR() / Math.PI * 180), 0, 0, 1.0f);
GL11.glTranslated(currentObject.getX(), currentObject.getY(), 0);
GL11.glBegin(GL11.GL_POLYGON);
for (int j = 0; j < mesh.size(); j++) {
GL11.glVertex2d(mesh.get(j).x, mesh.get(j).y);
}
GL11.glEnd();
GL11.glTranslated(-currentObject.getX(), -currentObject.getY(), 0);
GL11.glRotatef((float)(-currentObject.getR() / Math.PI * 180), 0, 0, 1.0f);
}
}
In renderFrameObjects() I apply a rotation, a translation, draw the object (mesh coordinates are relative to the object's x, y), reverse the translation, and reverse the rotation. Yet the effect it gives when an object rotates (on collision) is similar to when one would apply a translation then a rotation (ie. rotates around a point at a radius). I can't seem to be able to figure this one out having tried various combinations of transformations.
Any help would be appreciated.
That is beacause they are applied to the local coordinate system of the object, not the object itself.
So the rotate rotates the coordinate system and the translation is applied within that rotated coordinate system.
BTW: Don't undo your matrix changes by applying negative transformations. Roundoff error's will accumulate and it probably is also less efficient then using glPushMatrix and glPopMatrix

Categories

Resources