2011-11-04 11 views
5

En realidad tengo una aplicación para Android 1.5 con una clase GLSurfaceView que muestra un polígono cuadrado simple en la pantalla.¿Cómo mover un cuadro OpenGL con el dedo?

Quiero aprender a agregar una nueva funcionalidad, la funcionalidad de mover el cuadrado tocándolo con el dedo. Quiero decir que cuando el usuario toca el cuadrado y mueve el dedo, el cuadrado debe pegarse al dedo, hasta que el dedo suelte la pantalla.

Cualquier tutorial/código de ejemplo/ayuda será apreciado.

Mi código:

public class MySurfaceView extends GLSurfaceView implements Renderer { 
private Context context; 
private Square square; 
private float xrot;     //X Rotation 
private float yrot;     //Y Rotation 
private float zrot;     //Z Rotation 
private float xspeed;    //X Rotation Speed 
private float yspeed;    //Y Rotation Speed 
private float z = -1.15f;   //Profundidad en el eje Z 
private float oldX; //valor anterior de X, para rotación 
private float oldY; //valor anterior de Y, para rotación 
private final float TOUCH_SCALE = 0.2f;  //necesario para la rotación 

//create the matrix grabber object in your initialization code 
private MatrixGrabber mg = new MatrixGrabber();   

private boolean firstTimeDone=false; //true si la aplicación ya ha sido inicializada. 

public MySurfaceView(Context context, Bitmap image) { 
    super(context); 
    this.context = context; 
    setEGLConfigChooser(8, 8, 8, 8, 16, 0); //fondo transparente 
    getHolder().setFormat(PixelFormat.TRANSLUCENT); //fondo transparente 
    //Transformamos esta clase en renderizadora 
    this.setRenderer(this); 
    //Request focus, para que los botones reaccionen 
    this.requestFocus(); 
    this.setFocusableInTouchMode(true); 
    square = new Square(image);         
} 

public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
    gl.glDisable(GL10.GL_DITHER);    //dithering OFF 
    gl.glEnable(GL10.GL_TEXTURE_2D);   //Texture Mapping ON 
    gl.glShadeModel(GL10.GL_SMOOTH);   //Smooth Shading 
    gl.glClearDepthf(1.0f);      //Depth Buffer Setup 
    gl.glEnable(GL10.GL_DEPTH_TEST);   //Depth Testing ON 
    gl.glDepthFunc(GL10.GL_LEQUAL); 
    gl.glClearColor(0,0,0,0); //fondo transparente 
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);   
    //Cargamos la textura del cubo. 
    square.loadGLTexture(gl, this.context); 
} 

public void onDrawFrame(GL10 gl) { 
    //Limpiamos pantalla y Depth Buffer 
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 
    gl.glLoadIdentity(); 
    //Dibujado 
    gl.glTranslatef(0.0f, 0.0f, z);   //Move z units into the screen 
    gl.glScalef(0.8f, 0.8f, 0.8f);   //Escalamos para que quepa en la pantalla 
    //Rotamos sobre los ejes. 
    gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f); //X 
    gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f); //Y 
    gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f); //Z 
    //Dibujamos el cuadrado 
    square.draw(gl);  
    //Factores de rotación. 
    xrot += xspeed; 
    yrot += yspeed;   


    if (!firstTimeDone) 
    {  
     /////////////// NEW CODE FOR SCALING THE AR IMAGE TO THE DESIRED WIDTH /////////////////    
     mg.getCurrentProjection(gl); 
     mg.getCurrentModelView(gl);      
     float [] modelMatrix = new float[16]; 
     float [] projMatrix = new float[16]; 
     modelMatrix=mg.mModelView; 
     projMatrix=mg.mProjection;   
     int [] mView = new int[4]; 
     mView[0] = 0; 
     mView[1] = 0; 
     mView[2] = 800; //width 
     mView[3] = 480; //height 
     float [] outputCoords = new float[3]; 
     GLU.gluProject(-1.0f, -1.0f, z, modelMatrix, 0, projMatrix, 0, mView, 0, outputCoords, 0); 

     int i=0; 
     System.out.print(i); 
     // firstTimeDone=true; 
    } 
} 

//si el surface cambia, resetea la vista, imagino que esto pasa cuando cambias de modo portrait/landscape o sacas el teclado físico en móviles tipo Droid. 
public void onSurfaceChanged(GL10 gl, int width, int height) { 
    if(height == 0) {      
     height = 1;       
    } 
    gl.glViewport(0, 0, width, height);  //Reset Viewport 
    gl.glMatrixMode(GL10.GL_PROJECTION); //Select Projection Matrix 
    gl.glLoadIdentity();     //Reset Projection Matrix 
    //Aspect Ratio de la ventana 
    GLU.gluPerspective(gl, 45.0f, (float)width/(float)height, 0.1f, 100.0f); 
    gl.glMatrixMode(GL10.GL_MODELVIEW);  //Select Modelview Matrix 
    gl.glLoadIdentity();     //Reset Modelview Matrix   

} 

public boolean onTouchEvent(MotionEvent event) { 
    float x = event.getX(); 
    float y = event.getY(); 
    switch (event.getAction()) 
    { 
     case MotionEvent.ACTION_MOVE: 
      //Calculamos el cambio 
      float dx = x - oldX; 
      float dy = y - oldY; 
      xrot += dy * TOUCH_SCALE; 
      yrot += dx * TOUCH_SCALE; 
      //Log.w("XXXXXX", "ACTION_MOVE_NO_ZOOM"); 
      break; 
    } 
    oldX = x; 
    oldY = y; 
    return true; //El evento ha sido manejado 
} 

public void zoomIn(){ 
    z=z+0.2f; 
    if (z>-1.0f) 
     z=-1.0f; 
} 
public void zoomOut(){ 
    z=z-0.2f; 
    if (z<-20.0f) 
     z=-20.0f; 
} 
public void rotateL(){ 
    zrot=zrot+3.0f; 
} 
public void rotateR(){ 
    zrot=zrot-3.0f; 
} 
public void reset() 
{ 
    xrot=0; 
    yrot=0; 
    zrot=0; 
    xspeed=0; 
    yspeed=0; 
    z = -5.0f; 
} 
} 

Ésta es mi clase cuadrado:

public class Square { 
//Buffer de vertices 
private FloatBuffer vertexBuffer; 
//Buffer de coordenadas de texturas 
private FloatBuffer textureBuffer; 
//Puntero de texturas 
private int[] textures = new int[3]; 
//El item a representar 
private Bitmap image; 
//Definición de vertices 

private float vertices[] = 
{ 
    -1.0f, -1.0f, 0.0f,  //Bottom Left 
    1.0f, -1.0f, 0.0f,  //Bottom Right 
    -1.0f, 1.0f, 0.0f,  //Top Left 
    1.0f, 1.0f, 0.0f  //Top Right 
}; 
/* 
private float vertices[] = 
{ 
-0.8f, -0.8f, 0.0f,  //Bottom Left 
0.8f, -0.8f, 0.0f,  //Bottom Right 
-0.8f, 0.8f, 0.0f,  //Top Left 
0.8f, 0.8f, 0.0f 
}; 
*/ 
//Coordenadas (u, v) de las texturas  
/* 
private float texture[] = 
{   
    //Mapping coordinates for the vertices 
    0.0f, 0.0f, 
    0.0f, 1.0f, 
    1.0f, 0.0f, 
    1.0f, 1.0f 
}; 
*/ 
private float texture[] = 
{ 
    //Mapping coordinates for the vertices 
    0.0f, 1.0f, 
    1.0f, 1.0f, 
    0.0f, 0.0f, 
    1.0f, 0.0f 
}; 
//Inicializamos los buffers 
public Square(Bitmap image) { 
    ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4); 
    byteBuf.order(ByteOrder.nativeOrder()); 
    vertexBuffer = byteBuf.asFloatBuffer(); 
    vertexBuffer.put(vertices); 
    vertexBuffer.position(0); 

    byteBuf = ByteBuffer.allocateDirect(texture.length * 4); 
    byteBuf.order(ByteOrder.nativeOrder()); 
    textureBuffer = byteBuf.asFloatBuffer(); 
    textureBuffer.put(texture); 
    textureBuffer.position(0); 

    this.image=image; 
} 
//Funcion de dibujado 
public void draw(GL10 gl) { 
    gl.glFrontFace(GL10.GL_CCW); 
    //gl.glEnable(GL10.GL_BLEND); 
    //Bind our only previously generated texture in this case 
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); 
    //Point to our vertex buffer 
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); 
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer); 
    //Enable vertex buffer 
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 
    //Draw the vertices as triangle strip 
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length/3); 
    //Disable the client state before leaving 
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 
    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 
    //gl.glDisable(GL10.GL_BLEND);  
} 
//Carga de texturas 
public void loadGLTexture(GL10 gl, Context context) { 
    //Generamos un puntero de texturas 
    gl.glGenTextures(1, textures, 0);  
    //y se lo asignamos a nuestro array 
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); 
    //Creamos filtros de texturas 
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); 
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 
    //Diferentes parametros de textura posibles GL10.GL_CLAMP_TO_EDGE 
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); 
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);  
    /* 
    String imagePath = "radiocd5.png"; 
    AssetManager mngr = context.getAssets(); 
    InputStream is=null; 
    try { 
     is = mngr.open(imagePath); 
    } catch (IOException e1) { e1.printStackTrace(); } 
    */ 
    //Get the texture from the Android resource directory 
    InputStream is=null; 
    /* 
    if (item.equals("rim")) 
     is = context.getResources().openRawResource(R.drawable.rueda); 
    else if (item.equals("selector")) 
     is = context.getResources().openRawResource(R.drawable.selector); 
    */  
    /* 
    is = context.getResources().openRawResource(resourceId); 
    Bitmap bitmap = null; 
    try { 
     bitmap = BitmapFactory.decodeStream(is); 
    } finally { 
     try { 
      is.close(); 
      is = null; 
     } catch (IOException e) { 
     } 
    } 
    */ 
    Bitmap bitmap =image;  
    //con el siguiente código redimensionamos las imágenes que sean mas grandes de 256x256. 
    int newW=bitmap.getWidth(); 
    int newH=bitmap.getHeight(); 
    float fact; 
    if (newH>256 || newW>256) 
    { 
     if (newH>256) 
     { 
      fact=(float)255/(float)newH; //porcentaje por el que multiplicar para ser tamaño 256 
      newH=(int)(newH*fact); //altura reducida al porcentaje necesario 
      newW=(int)(newW*fact); //anchura reducida al porcentaje necesario 
     } 
     if (newW>256) 
     { 
      fact=(float)255/(float)newW; //porcentaje por el que multiplicar para ser tamaño 256 
      newH=(int)(newH*fact); //altura reducida al porcentaje necesario 
      newW=(int)(newW*fact); //anchura reducida al porcentaje necesario 
     } 
     bitmap=Bitmap.createScaledBitmap(bitmap, newW, newH, true); 
    }  
    //con el siguiente código transformamos imágenes no potencia de 2 en imágenes potencia de 2 (pot) 
    //meto el bitmap NOPOT en un bitmap POT para que no aparezcan texturas blancas. 
    int nextPot=256; 
    int h = bitmap.getHeight(); 
    int w = bitmap.getWidth(); 
    int offx=(nextPot-w)/2; //distancia respecto a la izquierda, para que la imagen quede centrada en la nueva imagen POT 
    int offy=(nextPot-h)/2; //distancia respecto a arriba, para que la imagen quede centrada en la nueva imagen POT 
    Bitmap bitmap2 = Bitmap.createBitmap(nextPot, nextPot, Bitmap.Config.ARGB_8888); //crea un bitmap transparente gracias al ARGB_8888 
    Canvas comboImage = new Canvas(bitmap2); 
    comboImage.drawBitmap(bitmap, offx, offy, null); 
    comboImage.save(); 

    //Usamos Android GLUtils para espcificar una textura de 2 dimensiones para nuestro bitmap 
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap2, 0); 

    //Checkeamos si el GL context es versión 1.1 y generamos los Mipmaps por Flag. Si no, llamamos a nuestra propia implementación 
    if(gl instanceof GL11) { 
     gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE); 
     GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap2, 0); 
    } else { 
     buildMipmap(gl, bitmap2); 
    } 
    //Limpiamos los bitmaps 
    bitmap.recycle(); 
    bitmap2.recycle(); 
} 
//Nuestra implementación de MipMap. Escalamos el bitmap original hacia abajo por factor de 2 y lo asignamos como nuevo nivel de mipmap 
private void buildMipmap(GL10 gl, Bitmap bitmap) { 
    int level = 0; 
    int height = bitmap.getHeight(); 
    int width = bitmap.getWidth(); 
    while(height >= 1 || width >= 1) { 
     GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0); 
     if(height == 1 || width == 1) { 
      break; 
     } 
     level++; 
     height /= 2; 
     width /= 2; 
     Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); 
     bitmap.recycle(); 
     bitmap = bitmap2; 
    } 
} 
} 

Respuesta

5

¿Has mirado en el código tutorial Android? Tienen algo muy similar a esto con ejemplos en OpenGL ES 1 y 2.

En el tutorial de OpenGL ES 1, hay una sección para manejar eventos táctiles. http://developer.android.com/resources/tutorials/opengl/opengl-es10.html#touch

Por lo tanto, le conviene modificar la sección AddMotion del comando glrotatef en gltranslatef;

edición

parece que está más interesado en la conversión de coordenadas de selección de objetos. Entonces, donde sea que toques en la pantalla, ahí es donde va la imagen (en lugar de tocar y arrastrar una imagen, lo que implicaría una selección). Y tu pregunta sobre winZ me hace pensar que estás intentando gluunproject. Si ese es el caso, ya conoce su winZ porque traduce la cámara de regreso del objeto por su variable "z". Como tu z es negativo, ¿por qué no pruebas esto?

Suponiendo que haya definido su GLWrapper para su GLSurfaceView en su actividad:

mGLView.setGLWrapper(new GLWrapper() { 
     public GL wrap(GL gl) { 
      return new MatrixTrackingGL(gl); 
     } 

    }); 

A continuación, en la subclase GLSurfaceView/Procesador ...

public float[] unproject(GL10 gl, float x, float y) { 
    mMatrixGrabber.getCurrentState(gl); 
    int[] view = {0,0,this.getWidth(), this.getHeight()}; 
    float[] pos = new float[4]; 
    float[] result = null; 
    int retval = GLU.gluUnProject(x, y, -z, 
      mMatrixGrabber.mModelView, 0, 
      mMatrixGrabber.mProjection, 0, 
      view, 0, 
      pos, 0); 
    if (retval != GL10.GL_TRUE) { 
     Log.e("unproject", GLU.gluErrorString(retval)); 
    } else { 
     result = new float[3]; 
     result[0] = pos[0]/pos[3]; 
     result[1] = pos[1]/pos[3]; 
     result[2] = pos[2]/pos[3]; 
     result = pos; 
    } 
    return result; 
} 

continuación, puede modificar su controlador TouchEvent para contener

switch (event.getAction()) 
    { 
     case MotionEvent.ACTION_MOVE: 
      //Calculamos el cambio 
      float dx = x - oldX; 
      float dy = y - oldY; 
      xrot += dy * TOUCH_SCALE; 
      yrot += dx * TOUCH_SCALE; 
      //Log.w("XXXXXX", "ACTION_MOVE_NO_ZOOM"); 
      touching = true; 
      break; 
     case MotionEvent.ACTION_UP: 
      xrot = 0; 
      yrot = 0; 
      zrot = 0; 
      touching = false; 
      break; 
    } 

Y coloque la siguiente sección en su método de dibujo antes las otras llamadas de conversión/escala/rotación:

if (touching) { 
     float[] point = unproject(gl, oldX, (this.getHeight() - oldY)); 
     if (point == null) { 
      Log.e("Draw", "No Point"); 
     } else { 
      gl.glTranslatef(point[0], point[1], 0); 
     } 
    } 

Espero que esto le dé el resultado que desea.

+0

sí, lo he comprobado hace unas horas, y no me puede ayudar en mis necesidades – NullPointerException

+0

¿En qué específicamente no te puede ayudar? Muestra cómo manejar las entradas y modificar la ubicación del objeto OpenGL en función de las entradas. ¿Quiso decir que quería arrastrar el objeto directamente? ¿Entonces su problema tiene más que ver con la conversión de coordenadas que con el manejo de entradas? Si ese es el caso, es posible que desee considerar el uso de una proyección ortográfica en 2d para la conversión de coordenadas universales. De lo contrario, debe hacer algunas operaciones matemáticas para calcular la posición en función de la profundidad de la cámara. – Marc

+0

Si tiene problemas con la conversión, aquí hay un enlace SO para el mismo problema: http://stackoverflow.com/questions/7437482/gluunproject-android-opengl-es-1-1-usage – Marc

1

que aplicarían un oyente ontouch la espera de una acción hacia abajo. Una vez que eso se active, coloque la posición del dedo a través de getrawx o y, y luego vuelva a dibujar el cuadrado en el lienzo abierto según corresponda. Aquí hay un enlace con un buen tutorial. http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-5-implementing-the-drag-gesture/1789?tag=content;siu-container

+0

no es tan fácil, las coordenadas de apertura no son las mismas que x, y la pantalla se conecta. Y se apreciará un poco más de ayuda que dos líneas – NullPointerException

+0

¿No se puede traducir el par de coordenadas en algo que OpenGL puede usar? Sé que he hecho algo similar con una biblioteca de gráficos diferente en Android. De hecho, es bastante fácil obtener el tamaño de la pantalla y asignar esas coordenadas relacionalmente a la pantalla. –

+0

no puedo aplicar ese tutorial a mi código, él está usando matrix, yo no, estoy usando una clase Square con otro tipo de implementación, voy a editar mi pregunta para agregar mi código en unos pocos segundos – NullPointerException

Cuestiones relacionadas