I'm learning Java and OpenGL ES for Android by reading tutorials and applying what I already know. And I have now hit a brick wall when it comes to rotating an object.
Rotating a cube by touching the screen is no problem. But if I rotate the cube 180 degrees up or down, then when I now try to rotate the cube left or right it is inverted. I know why this is happening but I cannot find a solution.
The code is below if some want to test it:
File "Rotating.java":
package com.test.opengl;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class Rotating extends Activity {
private GLControlView glControlView;
@Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
this.requestWindowFeature( Window.FEATURE_NO_TITLE );
getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN );
glControlView = new GLControlView( this );
setContentView( glControlView );
}
}
File "GLControlView.java":
package com.test.opengl;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLSurfaceView.Renderer;
import android.view.MotionEvent;
public class GLControlView extends GLSurfaceView implements Renderer {
private Context context;
private float xPrevious, yPrevious;
private float xRotation = 0.0f, yRotation = 0.0f;
private SimpleCubeObject cubeObject;
public GLControlView( Context context ) {
super( context );
this.context = context;
this.setRenderer( this );
this.requestFocus();
this.setFocusableInTouchMode( true );
cubeObject = new SimpleCubeObject();
}
public void onSurfaceCreated( GL10开发者_如何学Python gl, EGLConfig config ) {
gl.glClearColor( 0.0f, 0.0f, 0.0f, 0.5f );
gl.glShadeModel( GL10.GL_SMOOTH );
gl.glClearDepthf( 1.0f );
gl.glEnable( GL10.GL_DEPTH_TEST );
gl.glDepthFunc( GL10.GL_LEQUAL );
gl.glHint( GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST );
}
public void onDrawFrame( GL10 gl ) {
gl.glClear( GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT );
gl.glLoadIdentity();
gl.glTranslatef( 0.0f, 0.0f, -10.0f );
gl.glPushMatrix();
gl.glRotatef( xRotation, 1.0f, 0.0f, 0.0f );
gl.glRotatef( yRotation, 0.0f, 1.0f, 0.0f );
gl.glPushMatrix();
cubeObject.draw( gl );
gl.glPopMatrix();
gl.glPopMatrix();
}
public void onSurfaceChanged( GL10 gl, int width, int height ) {
gl.glViewport( 0, 0, width, height );
gl.glMatrixMode( GL10.GL_PROJECTION );
gl.glLoadIdentity();
GLU.gluPerspective( gl, 45.0f, ( ( float )width / ( float )height ), 0.1f, 100.0f );
gl.glMatrixMode( GL10.GL_MODELVIEW );
gl.glLoadIdentity();
}
@Override
public boolean onTouchEvent( MotionEvent event ) {
float xEvent = event.getX();
float yEvent = event.getY();
switch( event.getAction() ) {
case MotionEvent.ACTION_DOWN: {
xPrevious = xEvent;
yPrevious = yEvent;
return true;
}
case MotionEvent.ACTION_MOVE: {
float xDelta = xEvent - xPrevious;
float yDelta = yEvent - yPrevious;
xRotation += ( yDelta * 0.5f );
yRotation += ( xDelta * 0.5f );
xPrevious = xEvent;
yPrevious = yEvent;
return true;
}
default: return super.onTouchEvent( event );
}
}
}
File "SimpleCubeObject.java":
package com.test.opengl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.opengles.GL10;
public class SimpleCubeObject {
private int[] textures = new int[ 1 ];
private float[] colors = {
0.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
private short[] indices = {
0, 1, 2, 0, 2, 3,
1, 5, 6, 1, 6, 2,
2, 6, 7, 2, 7, 3,
3, 7, 4, 3, 4, 0,
0, 4, 5, 0, 5, 1,
7, 6, 5, 7, 5, 4
};
private float[] vertices = {
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f
};
private FloatBuffer colorBuffer;
private ShortBuffer indexBuffer;
private FloatBuffer vertexBuffer;
public SimpleCubeObject() {
ByteBuffer cbb = ByteBuffer.allocateDirect( colors.length * 4 );
cbb.order( ByteOrder.nativeOrder() );
colorBuffer = cbb.asFloatBuffer();
colorBuffer.put( colors );
colorBuffer.position( 0 );
ByteBuffer ibb = ByteBuffer.allocateDirect( indices.length * 2 );
ibb.order( ByteOrder.nativeOrder() );
indexBuffer = ibb.asShortBuffer();
indexBuffer.put( indices );
indexBuffer.position( 0 );
ByteBuffer vbb = ByteBuffer.allocateDirect( vertices.length * 4 );
vbb.order( ByteOrder.nativeOrder() );
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put( vertices );
vertexBuffer.position( 0 );
}
public void draw( GL10 gl ) {
gl.glFrontFace( GL10.GL_CCW );
gl.glEnable( GL10.GL_CULL_FACE );
gl.glCullFace( GL10.GL_BACK );
gl.glBindTexture( GL10.GL_TEXTURE_2D, textures[ 0 ] );
gl.glEnableClientState( GL10.GL_COLOR_ARRAY );
gl.glEnableClientState( GL10.GL_TEXTURE_COORD_ARRAY );
gl.glColorPointer( 4, GL10.GL_FLOAT, 0, colorBuffer );
gl.glVertexPointer( 3, GL10.GL_FLOAT, 0, vertexBuffer );
gl.glDrawElements( GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_SHORT, indexBuffer );
gl.glDisableClientState( GL10.GL_VERTEX_ARRAY );
gl.glDisableClientState( GL10.GL_COLOR_ARRAY );
gl.glDisable( GL10.GL_CULL_FACE );
}
}
I sure hope someone can help me with this. I believe, as always, that the solution is simple and easy - it is just me who can't see it right now.
This problem is inherent to Euler angles representation of rotation (that is storing rotation as rotations from the reference axes) as each subsequent part of rotation changes the reference frame. You could try using some different representation of object rotation, like quaternions or - per Ishtar's suggestion - rotation matrix or axis/angle.
Here is a tutorial on quaternions if you would like to try them: http://gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation
And also some different suggestions: http://gpwiki.org/forums/viewtopic.php?t=8611&sid=7d8cb26617084c80c670634d3d7e9f36
http://www.gamedev.net/community/forums/topic.asp?topic_id=491391
I think it's the order that you are doing the rotations in. Have you tried this way:
gl.glRotatef( yRotation, 0.0f, 1.0f, 0.0f );
gl.glRotatef( xRotation, 1.0f, 0.0f, 0.0f );
It's not simple.
xRotation += ( yDelta * 0.5f );
yRotation += ( xDelta * 0.5f );
This is not going to work. Either your x or y-axis won't be correct, these are the pitch and roll-axis(?). Not the x and y-axis.
I think you'll need to remember the rotation matrix itself, not some x and y rotations. You better have only one axis you are rotating about. The axis of course depends on the direction of the MotionEvent. And the rotation amount on the total dragged distance.
float xDelta = xEvent - xActionDown;//from starting point
float yDelta = yEvent - yActionDown;
float distance = sqrt(xDelta*xDelta+yDelta*yDelta);
float xaxis = xDelta/distance;//normalized: 0.0 <-> 1.0
float yaxis = yDelta/distance;
gl.glRotatef( distance, yaxis, xaxis, 0.0f );//x and y swapped!
I am 100% sure the above is incorrect. You'll have to add some checks, a minus, etc. But I hope you get the basic idea?
精彩评论