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);
}
}
Related
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;
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.
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;
}
i have an application that shows a simple square polygon represented on a GLSurfaceView. Actually i can do zoomIn and zoomOut with two buttons.
But now i want to implemente the iphone multitouch style zoomIn and zoomOut. It means that when the user presses with two fingers the screen and move one finger into another, the user is doing zoomOut, and when the user separates the fingers makes zoomIn.
How can i implement this with Android, openglES, and GLsurfaceView?
the class:
public class MySurfaceView extends GLSurfaceView implements Renderer {
private Context context;
private Square square;
/* Rotation values */
private float xrot; //X Rotation
private float yrot; //Y Rotation
/* Rotation speed values */
private float xspeed; //X Rotation Speed ( NEW )
private float yspeed; //Y Rotation Speed ( NEW )
private float z = -5.0f; //Depth Into The Screen ( NEW )
/*
* These variables store the previous X and Y
* values as well as a fix touch scale factor.
* These are necessary for the rotation transformation
* added to this lesson, based on the screen touches. ( NEW )
*/
private float oldX;
private float oldY;
private final float TOUCH_SCALE = 0.2f; //Proved to be good for normal rotation ( NEW )
public MySurfaceView(Context context) {
super(context);
this.context = context;
setEGLConfigChooser(8, 8, 8, 8, 16, 0); //fondo transparente
getHolder().setFormat(PixelFormat.TRANSLUCENT); //fondo transparente
//Set this as Renderer
this.setRenderer(this);
//Request focus, otherwise buttons won't react
this.requestFocus();
this.setFocusableInTouchMode(true);
square = new Square();
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glDisable(GL10.GL_DITHER); //Disable dithering ( NEW )
gl.glEnable(GL10.GL_TEXTURE_2D); //Enable Texture Mapping
gl.glShadeModel(GL10.GL_SMOOTH); //Enable Smooth Shading
gl.glClearDepthf(1.0f); //Depth Buffer Setup
gl.glEnable(GL10.GL_DEPTH_TEST); //Enables Depth Testing
gl.glDepthFunc(GL10.GL_LEQUAL); //The Type Of Depth Testing To Do
gl.glClearColor(0,0,0,0); //fondo transparente
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
//Load the texture for the cube once during Surface creation
square.loadGLTexture(gl, this.context);
}
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;
}
/**
* If the surface changes, reset the view
*/
public void onSurfaceChanged(GL10 gl, int width, int height) {
if(height == 0) { //Prevent A Divide By Zero By
height = 1; //Making Height Equal One
}
gl.glViewport(0, 0, width, height); //Reset The Current Viewport
gl.glMatrixMode(GL10.GL_PROJECTION); //Select The Projection Matrix
gl.glLoadIdentity(); //Reset The Projection Matrix
//Calculate The Aspect Ratio Of The Window
GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW); //Select The Modelview Matrix
gl.glLoadIdentity(); //Reset The Modelview Matrix
}
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;
}
public void zoomIn(){ z=z+0.2f; }
public void zoomOut(){ z=z-0.2f; }
}
done using this method: http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-6-implementing-the-pinch-zoom-gesture/1847
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 :)