Touch coords and OpenGL space coords - android

I'm having a hard time implementing the rotation and movement of an on-screen OpenGL shape. Basically, what I want to achieve is being able to control the shape using a touch screen. Wherever I touch, it should rotate to that direction and start moving towards until it gets there.
Here's the code that sets up the frustum and the camera in onSurfaceChanged():
glViewport(0, 0, width, height);
float sizeRatio = (float) width / height;
Matrix.frustumM(
projectionMatrix, 0, -sizeRatio, sizeRatio, -1.0f, 1.0f, 1.0f, 20.0f
);
Matrix.setLookAtM(
viewMatrix, 0, 0, 0, -60, 0f, 0f, 0f, 0.0f, 1.0f, 0.0f
);
Matrix.multiplyMM(globalMvpMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
and here's how the touch input is handled in onTouchEvent() (shape is an object that stores position and rotation and then draws the shape on the screen):
lastTouchX = event.getX();
lastTouchY = event.getY();
float shapePosX = shape.getPositionX();
float shapePosY = shape.getPositionY();
int[] viewport = new int[4];
glGetIntegerv(GL_VIEWPORT, viewport, 0);
float[] unprojectedTouchCoords = new float[4];
GLU.gluUnProject(
lastTouchX, lastTouchY, 0,
viewMatrix, 0,
projectionMatrix, 0,
viewport, 0,
unprojectedTouchCoords, 0
);
float unprojectedX = unprojectedTouchCoords[0] / unprojectedTouchCoords[3];
float unprojectedY = unprojectedTouchCoords[1] / unprojectedTouchCoords[3];
float rotation = 90 - (float) Math.toDegrees(Math.atan2(
shapePosY - unprojectedY, shapePosX - unprojectedX
));
shape.setRotation(rotation);
float moveX = 2 * (float) Math.cos(Math.toRadians(rotation));
float moveY = 2 * (float) Math.sin(Math.toRadians(rotation));
shape.move(moveX, moveY);
However, it doesn't seem to work well. The shape is moving in the wrong direction, and the rotation is only correct if the position of the shape is (0, 0). In any other case, it breaks.
I guess this problem involves distance between the camera and the shape in the OpenGL space, but I have no idea where and how to fix that. I tried bringing the camera closer to the shape but with no apparent improvement.
Can you please help me with this one? I'm getting really frustrated.

I haven't looked deeply at your code but I really want to give you a suggestion: the order of transformations matters in OpenGL, because they are cumulative.
This means that rotate and translate is different than translate and rotate.
Here is a good starting point to understand what I'm saying.

Seems like you have co-ordinates calculation problem. You need to #Override the onTouchEvent method of GLSurfaceView, rest is below
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float mPreviousX;
private float mPreviousY;
#Override
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
// reverse direction of rotation above the mid-line
if (y > getHeight() / 2) {
dx = dx * -1 ;
}
// reverse direction of rotation to left of the mid-line
if (x < getWidth() / 2) {
dy = dy * -1 ;
}
mRenderer.setAngle(
mRenderer.getAngle() +
((dx + dy) * TOUCH_SCALE_FACTOR)); // = 180.0f / 320
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}

Related

Android OpenGL ES rotate 3D object with touch event

This is an Android application. I want to rotate a sphere with touch event using Matrix in OpenGLES 2.0. However the angle is not right. It seems like the rotate is not stable. I want to rotate only x when I touch horizontally, and rotate only y when I touch vertically. But in fact, when I touch horizontally it rotate x and y, so did when I touch vertically... Something wrong with the calculation of rotate anglex and angley or the Matrix set up?
Here is the touch event:
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
mRenderer.setAngleX(mRenderer.getAngleX() + (dx * TOUCH_SCALE_FACTOR));
mRenderer.setAngleY(mRenderer.getAngleY() - (dy * TOUCH_SCALE_FACTOR));
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
This is the Matrix set up:
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//set up the matrix
float [] mTempMatrix = new float[16];
Matrix.setIdentityM(mModelMatrix, 0); // initialize to model matrix
Matrix.setIdentityM(mTranslateMatrix,0);
Matrix.translateM(mTranslateMatrix, 0, 0.0f, 0.0f, 0.0f);
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix,0,mTempMatrix,0,mTranslateMatrix,0);
Matrix.setIdentityM(mRotationMatrix,0);
Matrix.rotateM(mRotationMatrix, 0, mAngleX, 0,1,0);
Matrix.rotateM(mRotationMatrix, 0, mAngleY, 1,0,0);
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix,0,mTempMatrix,0,mRotationMatrix,0);
Matrix.setIdentityM(mScaleMatrix,0);
Matrix.scaleM(mScaleMatrix,0, 1f, 1f, 1f);
mTempMatrix = mModelMatrix.clone();
Matrix.multiplyMM(mModelMatrix,0,mTempMatrix,0,mScaleMatrix,0);
Matrix.setLookAtM(mViewMatrix, 0,
mLocation.x,mLocation.y,mLocation.z,
mTarget.x,mTarget.y,mTarget.z,
mUp.x,mUp.y,mUp.z);//camera
mTempMatrix = mViewMatrix.clone();
Matrix.multiplyMM(mViewMatrix, 0, mTempMatrix,0,mModelMatrix,0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
mSphere.draw(mMVPMatrix);
}
It seems to me that since you use
mRenderer.setAngleX(mRenderer.getAngleX() + (dx * TOUCH_SCALE_FACTOR));
Even if dx is 0, any previous angle (through mRenderer.getAngleX() ) will be re-applied in the next iteration.
Let dx alone modify mAngleX instead (and do the same for mAngleY of course):
mRenderer.mAngleX += dx * TOUCH_SCALE_FACTOR;

OpenGL inversion in y-axis

I'm using the OpenGL touch events to move shapes but what happens is the shapes on the opposite side of the screen move (x-axis). So if you try to move a shape at the bottom, then a shape at the top will move inside. The top right corner is (0,480) and the bottom left (800,0). I've tried changing the numbers round inthe view matrix but it hasnt worked. Why is this happening?
Im sure I've set my view and projection matrices correctly. Here they are.
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background clear color to gray.
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
GLES20.glFrontFace(GLES20.GL_CCW); // Counter-clockwise winding.
GLES20.glEnable(GLES20.GL_CULL_FACE);// Use culling to remove back faces.
GLES20.glCullFace(GLES20.GL_BACK);// What faces to remove with the face culling.
GLES20.glEnable(GLES20.GL_DEPTH_TEST);// Enable depth testing
// Position the eye behind the origin.
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = -3.0f;
// We are looking toward the distance
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = 0.0f;
// Set our up vector. This is where our head would be pointing were we holding the camera.
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.0f;
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
Matrix.setIdentityM(mViewMatrix, 0);
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Sets the current view port to the new size.
GLES20.glViewport(0, 0, width, height);
float RATIO = (float) width / (float) height;
// this projection matrix is applied to object coordinates in the onDrawFrame() method
Matrix.frustumM(mProjectionMatrix, 0, -RATIO, RATIO, -1, 1, 1, 10);
Matrix.setIdentityM(mProjectionMatrix, 0);
}
Update
The view seems to render properly. And the shape will appear on the screen where i want them to, if i translate them, or change the vertex coordinates slightly. Whats not right is how it registers the touch events. Any ideas?
This is how i check the touch events.
if(shapeW < minX){minX = shapeW;}
if(shapeW > maxX){maxX = shapeW;}
if(shapeH < minY){minY = shapeH;}
if(shapeH > maxY){maxY = shapeH;}
//Log.i("Min&Max" + (i / 4), String.valueOf(minX + ", " + maxX + ", " + minY + ", " + maxY));
if(minX < MyGLSurfaceView.touchedX && MyGLSurfaceView.touchedX < maxX && minY < MyGLSurfaceView.touchedY && MyGLSurfaceView.touchedY < maxY)
{
xAng[j] = xAngle;
yAng[j] = yAngle;
Log.i("cube "+j, " pressed");
}
From the origin, the z-axis is positive coming towards you and negative going away into the screen. So if my assumption is correct that your shapes are drawn in the z = 0 plane, your eye is actually positioned behind them. Hence if you move an object one way it appears to move the other way. Try using a positive value for eyeZ instead.
For example, eye = (0, 0, 3), look = (0, 0, 0) would position the eye out of the origin towards you looking down into the screen. In contrast, using eye = (0, 0, -3), look = (0, 0, 0) would put the eye into the screen looking back out of it.

Find new coordinates of overlay object on the canvas after modifying on matrix in Android

I'm working on Android project and I'm using View class to drawing image and overlays.
I'm using this code to drawing image and circle (it is working fine):
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if (!isInitialized) {
w = getWidth();
h = getHeight();
position.set(w / 2, h / 2);
// Scaling image to be fit screen
if(width<=height) { scale=((float)w)/width; } else { scale=((float)h)/height; }
isInitialized = true;
}
Paint paint = new Paint();
matrix.reset();
matrix.postTranslate(-width / 2.0f, -height / 2.0f);
matrix.postRotate((float)(angle*180/Math.PI));
matrix.postScale(scale, scale);
matrix.postTranslate(position.getX(), position.getY());
canvas.drawBitmap(bitmap, matrix, paint);
canvas.concat(matrix);
canvas.drawCircle(testPoint.x, testPoint.y, 20, paint );
}
Now I want to show PopupWindow windows as a tip on the image and on the same position of circle (testPoint). but the PopupWindows is not drawable object, so how can I find the new coordinates of testPoint after scaling, rotation and translating to put the coordinates of the PopupWindow same.
I'm trying to write code and it working fine, but for rotation and translating only without scaling, this is the code I wrote:
int offsetX=(int) (-(width / 2.0f)+position.getX())+rc.left;
int offsetY=(int) (-(height / 2.0f)+position.getY())+rc.top;
Point RotatedPoint = RotatePoint(testPoint, centerPoint, angle);
RotatedPoint.x +=offsetX;
RotatedPoint.y +=offsetY;
popup.update(RotatedPoint.x, RotatedPoint.y, -1, -1);
Where rc is the offset difference between bitmap view coordinates and the screen coordinates.
And I'm tested RotatePoint function and it working correctly.
Note: when the scale is equal 1 (disable scaling) the position of popup window is updating correctly if I rotate or move the image.
How can I merged the scale (if not equal 1 only) with equations?
Or there is another way to find new coordinates of overlay object on the canvas after modifying on matrix?
Please help me to solved this problem.
I will be grateful for the help.
Thank you.
try to use matrix.mapPoints()
u must have the absolute coordinates of the image
how to absolute coord?
void calculaCoordenadasImagen(MotionEvent e){
float []m = new float[9];
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X] * -1;
float transY = m[Matrix.MTRANS_Y] * -1;
float scaleX = m[Matrix.MSCALE_X];
float scaleY = m[Matrix.MSCALE_Y];
lastTouchX = (int) ((e.getX() + transX) / scaleX);
lastTouchY = (int) ((e.getY() + transY) / scaleY);
lastTouchX = Math.abs(lastTouchX);
lastTouchY = Math.abs(lastTouchY);
}
then use 2 different arrays to save the points
int [] absolute = new int[2];
absolute[0]=lastTouchX;
absolute[1]=lastTouchY;
int [] points = new int[2];
points = matrix.mapPoints(absolute)
in absolute u have the absolute coordinates and in points u have the points u want know
i wish it help!

How to rotate circularly (360ยบ) a polygon (square) touching the screen?

Actually i have an application that creates a square (a polygon, not a cube, a square) and the user can rotate it touching the screen, but something is going bad, because the rotation is not fair, it only can rotate it horizontally and vertically, but not circularly. I need that the user can rotate it circularly too.
I use this code to do the rotation (horizontal/vertical)
public void onDrawFrame(GL10 gl) {
//Clear Screen And Depth Buffer
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity(); //Reset The Current Modelview Matrix
//Drawing
gl.glTranslatef(0.0f, 0.0f, z); //Move z units into the screen
gl.glScalef(0.8f, 0.8f, 0.8f); //Scale the Cube to 80 percent, otherwise it would be too large for the screen
//Rotate around the axis based on the rotation matrix (rotation, x, y, z)
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y
square.draw(gl); //Draw the Cube
//Change rotation factors
xrot += xspeed;
yrot += yspeed;
}
public boolean onTouchEvent(MotionEvent event) {
//
float x = event.getX();
float y = event.getY();
//If a touch is moved on the screen
if(event.getAction() == MotionEvent.ACTION_MOVE) {
//Calculate the change
float dx = x - oldX;
float dy = y - oldY;
//Define an upper area of 10% on the screen
int upperArea = this.getHeight() / 10;
//Zoom in/out if the touch move has been made in the upper
if(y < upperArea) {
z -= dx * TOUCH_SCALE / 2;
//Rotate around the axis otherwise
} else {
xrot += dy * TOUCH_SCALE;
yrot += dx * TOUCH_SCALE;
}
//A press on the screen
} else if(event.getAction() == MotionEvent.ACTION_UP) {
//Define an upper area of 10% to define a lower area
int upperArea = this.getHeight() / 10;
int lowerArea = this.getHeight() - upperArea;
//Change the light setting if the lower area has been pressed
if(y > lowerArea) {
}
}
//Remember the values
oldX = x;
oldY = y;
//We handled the event
return true;
}
You should try this:
yourRootLayout.setOnClickListener(yourListener);
yourAnimation = new RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
yourAnimation.setDuration(duration in milliseconds - int);
#Override
protected void onClick(View view){
if (view.getId() == R.id.yourRootLayoutId){
yourRootLayout.startAnimation(yourAnimation);
}
}

Hud with shaders (opengl-es 2.0)

How to draw a HUD using shaders on opengl es 2.0?
I have a shader which draws a textured quad on screen, it uses MVP matrix. The quad has it own vertices which are independent of view position and so on (cause of MVP matrix)
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3f, 17);
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
I'd like to show the same quad on the top right corner (like a button or something else, HUD).
As I understand, i need create an ortho matrix instead of "frustumM", but what should i do later? How vertex shader should use vertices of quad?
Ok, You have your ortho matrix and quad, so whats the problem, translate modelview matrix of your quad to desired position (x,y,z=0), multiply it by ortho matrix , pass multiplied matrix to vertex shader, multiply vert position by your matrix and done :), i am not using any lookat function in my code to do this, but i have own code for matrices computation its partially code from some bada tutorial, for projection matrix i have other function.
void
Letter::Ortho(Matrix* result, float fovy, float aspect, float nearZ, float farZ)
{
GLfloat frustumW, frustumH;
frustumH = tanf(fovy / 360.0f * PI) * nearZ;
frustumW = frustumH * aspect;
Frustum(result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ);
}
void
Letter::LoadIdentity(Matrix* result)
{
memset(result, 0x0, sizeof(Matrix));
result->m[0][0] = 1.0f;
result->m[1][1] = 1.0f;
result->m[2][2] = 1.0f;
result->m[3][3] = 1.0f;
}
void
Letter::Frustum(Matrix *result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
float deltaX = right - left;
float deltaY = top - bottom;
float deltaZ = farZ - nearZ;
Matrix frustum;
if ((nearZ <= 0.0f) || (farZ <= 0.0f) ||
(deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f))
{
return;
}
frustum.m[0][0] = 2.0f * nearZ / deltaX;
frustum.m[0][1] = frustum.m[0][2] = frustum.m[0][3] = 0.0f;
frustum.m[1][1] = 2.0f * nearZ / deltaY;
frustum.m[1][0] = frustum.m[1][2] = frustum.m[1][3] = 0.0f;
frustum.m[2][0] = (right + left) / deltaX;
frustum.m[2][1] = (top + bottom) / deltaY;
frustum.m[2][2] = -(nearZ + farZ) / deltaZ;
frustum.m[2][3] = -1.0f;
frustum.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
frustum.m[3][0] = frustum.m[3][1] = frustum.m[3][3] = 0.0f;
Multiply(result, &frustum, result);
}
So, with this code :
LoadIdentity(&matPerspective);
Ortho(&matPerspective, 60.0f, TEXMANAGER.aspect, -1.0f, 20.0f);
LoadIdentity(&matModelview);
Translate(&matModelview, x ,y ,z);
Scale(&matModelview,size);
//Rotate(&matModelview, 0.0f, 1.0f, 0.0f, 1.0f);
Multiply(&posMatrix, &matModelview, &matPerspective);
And pass posMatrix to shader :)

Categories

Resources